Compare commits

...

140 Commits

Author SHA1 Message Date
Faruk AYDIN
067ec2eb9c Release v0.8.0 2023-08-02 15:07:27 +02:00
Faruk AYDIN
2d332b32d9 chore: Update version to 0.8.0 in Dockerfiles 2023-08-02 15:06:49 +02:00
Ömer Faruk Aydın
1d9ad2ba86 Merge pull request #1184 from automatisch/notion-find-database-item
feat(notion): add find database item action
2023-08-01 13:54:09 +02:00
Ömer Faruk Aydın
a28e2177f7 Merge pull request #1183 from automatisch/notion-create-page
feat(notion): add create page action
2023-08-01 13:44:47 +02:00
Ömer Faruk Aydın
18fe0df691 Merge pull request #1185 from automatisch/email-case-insensitive-login
fix(auth): allow login with case insensitive email
2023-07-31 17:05:01 +03:00
Ömer Faruk Aydın
8e21a06d99 Merge pull request #1186 from automatisch/gitlab-use-user-projects
fix(gitlab/list-projects): list projects the user has membership
2023-07-31 17:03:25 +03:00
Ali BARIN
2daf5473bb fix(gitlab/list-projects): list projects the user has membership 2023-07-31 16:00:27 +02:00
Ömer Faruk Aydın
928ff53adf Merge pull request #1187 from automatisch/fix-gitlab-github-names
fix: GitHub and GitLab app names
2023-07-31 16:54:20 +03:00
Faruk AYDIN
a71e95e6e5 fix: GitHub and GitLab app names 2023-07-31 15:47:06 +02:00
Ali BARIN
cb4a54b5cc fix(auth): allow login with case insensitive email 2023-07-30 14:59:16 +00:00
Rıdvan Akca
37e4524156 feat(notion): add find database item action 2023-07-27 14:39:57 +03:00
dependabot[bot]
9ac24ee051 chore(deps): bump word-wrap from 1.2.3 to 1.2.4
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-26 22:51:23 +02:00
Ömer Faruk Aydın
f28ccd559a Merge pull request #1177 from automatisch/notion-create-database-item
feat(notion): add create database item action
2023-07-25 14:04:08 +03:00
Ömer Faruk Aydın
8e84a93d8e Merge pull request #1166 from automatisch/create-worksheet
feat(google-sheets): add create worksheet action
2023-07-25 13:58:41 +03:00
Ömer Faruk Aydın
d871dec1b7 Merge pull request #1179 from automatisch/add-http-proxy-agent
fix(axios): incorporate http(s)-proxy-agents
2023-07-24 15:40:22 +03:00
Ömer Faruk Aydın
b133e1a197 Merge pull request #1176 from automatisch/compute-params
fix: allow colon while computing step parameters
2023-07-24 15:33:16 +03:00
Rıdvan Akca
9346a037b9 feat(notion): add create page action 2023-07-24 14:50:53 +03:00
Ali BARIN
89facbcddd fix(axios): incorporate http(s)-proxy-agents 2023-07-17 22:23:48 +00:00
Faruk AYDIN
53fef35638 fix: Allow colon while computing step parameters 2023-07-17 18:19:54 +02:00
Rıdvan Akca
bfe496a09b feat(notion): add create database item action 2023-07-17 16:23:00 +03:00
dependabot[bot]
ff774c2e8e chore(deps): bump semver from 5.7.1 to 5.7.2
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-14 14:31:07 +02:00
Ömer Faruk Aydın
08a6d1078c Merge pull request #1132 from automatisch/show-webhook-url-by-flag
feat: introduce singleton webhook URL
2023-07-13 12:02:49 +02:00
Ömer Faruk Aydın
e9bcc919bf Merge branch 'main' into show-webhook-url-by-flag 2023-07-13 11:58:19 +02:00
Faruk AYDIN
04f4693c85 fix: Fetch webhooks by flow id 2023-07-12 14:05:46 +02:00
Ömer Faruk Aydın
2a58a0a4c4 Merge pull request #1174 from automatisch/twilio-receive-sms-fix
fix(twilio): Receive SMS webhook payload
2023-07-12 11:24:14 +02:00
Faruk AYDIN
d911843648 fix(twilio): Receive SMS webhook payload 2023-07-11 17:50:31 +02:00
Ömer Faruk Aydın
c80d178410 Merge pull request #1170 from automatisch/mattermost-docs
docs(mattermost): Fix links of mattermost app
2023-07-05 14:33:19 +02:00
Faruk AYDIN
9fb4dca39b docs(mattermost): Fix links of mattermost app 2023-07-05 14:29:19 +02:00
Rıdvan Akca
0dd444d50b feat(google-sheets): add create worksheet action 2023-06-30 19:03:54 +03:00
dependabot[bot]
f3bf418997 chore(deps): bump fast-xml-parser from 4.2.4 to 4.2.5 (#1164)
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.2.4 to 4.2.5.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.2.4...v4.2.5)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-29 17:51:47 +02:00
KrzysztofDK
676027245f feat(mattermost): add auth and send message to channel action
* feat: mattermost integration

* post review adjustments
2023-06-29 16:45:57 +02:00
Ali BARIN
6c5039f1ba fix(odoo): add missing empty type file (#1165) 2023-06-29 16:03:44 +02:00
Jack Dane
807be59f25 feat(odoo): add auth and create lead action (#1143)
* Add Odoo App and Icon

* Add Auth for Odoo

* Authorise with API key, the password would also work, but we should encourage an API key.

* Odoo Verify Credentials method

* Add the xmlrpc dependency so the backend can communicate with Odoo's API.
* Add a port to the auth fields to establish a connection that might not be over HTTPS.

* Add still verified method

* Currently no need to keep uid, so remove it from the auth data.
* Await the callback from the xmlrpc method call to ensure we don't verify credentials before the callback has been executed.

* Add Odoo create-lead action

* Provide basic functionality to create a lead.

* Add Odoo type option

* Let the user decide if the lead should be a "lead" or "opportunity" in the create-lead action.

* Add documentation for Odoo app

* Follow project standards

* Change indents to 2 spaces
* Use single quotes instead of double

* Commonise the authentication method (DRY)

* Use latest for API doc link

* refactor(odoo): abstract and organize implementation

---------

Co-authored-by: Ali BARIN <ali.barin53@gmail.com>
2023-06-29 15:56:20 +02:00
AnimatedSwine37
8e9896ec2e fix(postgresql): close connections when done 2023-06-27 18:57:18 +02:00
Rıdvan Akca
110c2dbac8 docs: add missing dots in action/trigger descriptions 2023-06-26 12:05:42 +02:00
Rıdvan Akca
f55ec4bd8a fix(google-sheets): sort actions 2023-06-25 15:50:03 +02:00
AnimatedSwine37
06c9bf420e fix(discord): show announcement channels in selection 2023-06-25 13:31:47 +02:00
Rıdvan Akca
3c9bc53a79 feat(google-sheets): add create spreadsheet action 2023-06-23 20:29:12 +02:00
Ali BARIN
de7a35dfe9 feat: introduce singleton webhook URL 2023-06-22 08:51:43 +00:00
Ömer Faruk Aydın
92638c2e97 Merge pull request #1142 from automatisch/fix-twilio-receive-sms-test-run
fix(twilio/receive-sms): use phone number via phone number sid
2023-06-14 13:42:11 +02:00
Ali BARIN
63251e6a9a fix(twilio/receive-sms): use phone number via phone number sid 2023-06-14 13:27:49 +02:00
Ömer Faruk Aydın
59844c33fd Merge pull request #1140 from automatisch/notion-app
feat(notion): add auth and new DB items trigger
2023-06-14 13:24:59 +02:00
Faruk AYDIN
d36dd2ece1 docs: Use triggers link of Notion for available apps 2023-06-14 13:21:34 +02:00
Ömer Faruk Aydın
1fdb94739b Merge pull request #1141 from automatisch/fix-dynamic-data
fix: skip prior execution steps if no prior execution
2023-06-14 12:54:34 +02:00
Ali BARIN
8a18f4c44f fix: skip prior execution steps if no prior execution 2023-06-13 20:56:39 +00:00
Ali BARIN
c9c47c5519 docs(notion): add auth and new DB items trigger 2023-06-13 05:56:33 +00:00
Ali BARIN
6be8b55daa feat(notion): add auth and new DB items trigger 2023-06-13 05:56:33 +00:00
Ali BARIN
f2dc2f5530 feat: introduce CustomAutocomplete with variables 2023-06-12 14:06:13 +02:00
Ömer Faruk Aydın
42842e7aec Merge pull request #1130 from gh-kdk/feature/gitlab-integration-documentation
docs(gitlab): add connection and triggers
2023-06-12 12:31:43 +02:00
Faruk AYDIN
49d9f77d1b docs: Add GitLab to available apps 2023-06-12 12:31:07 +02:00
Faruk AYDIN
d06a89564f docs: Add GitLab favicon 2023-06-12 12:31:07 +02:00
Krzysztof Dukszta-Kwiatkowski
58a8510d49 feat: gitlab integration documentation 2023-06-12 12:31:07 +02:00
Ömer Faruk Aydın
8055d6555e Merge pull request #1139 from automatisch/update-flow-status-accordingly
fix: update flow.active when remote calls succeed
2023-06-12 11:48:25 +02:00
Ömer Faruk Aydın
39620d3510 Merge pull request #1135 from shehabghazy/postgres-docs
docs(postgres): add connection and actions
2023-06-12 11:15:11 +02:00
Faruk AYDIN
6d19711926 docs: Add PostgreSQL to available apps 2023-06-12 11:13:09 +02:00
Faruk AYDIN
0b362dd435 docs: Use postgresql key name instead of postgres 2023-06-12 11:11:39 +02:00
Shehab Ghazy
9485731e7d docs: Add PostgreSQL documentation 2023-06-12 11:08:17 +02:00
Ali BARIN
1449fb0f84 fix: update flow.active when remote calls succeed 2023-06-11 20:05:00 +00:00
Faruk AYDIN
e548dd49ca chore: Use paddle sandbox for all non-prod cloud envs 2023-06-08 19:38:00 +02:00
dependabot[bot]
35863ee6e9 chore(deps): bump fast-xml-parser from 4.0.11 to 4.2.4
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.0.11 to 4.2.4.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/commits)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-06 21:15:17 +02:00
Faruk AYDIN
0784a2d4d0 refactor: Use limit to prevent fetching all records 2023-06-06 12:59:33 +02:00
Ali BARIN
75d5c0e356 feat: prevent from being used in iframe 2023-06-06 12:59:21 +02:00
Ali BARIN
a2dd6d76a8 fix(webhook/handler): log whole computed payload 2023-06-06 12:59:12 +02:00
dependabot[bot]
bdc6b59857 chore(deps): bump vite from 3.1.8 to 3.2.7
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 3.1.8 to 3.2.7.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v3.2.7/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v3.2.7/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-06 09:33:26 +02:00
Ömer Faruk Aydın
34e95f1e89 Merge pull request #1126 from automatisch/optimize-last-execution
refactor: Optimize fetching last execution step
2023-06-05 23:11:16 +02:00
Faruk AYDIN
6a92cfc573 refactor: Optimize fetching last execution step 2023-06-05 23:00:35 +02:00
Rıdvan Akca
9f759d70b6 feat(google-sheets): add new spreadsheet rows trigger 2023-06-02 15:51:36 +02:00
Ali BARIN
43e957e8d3 feat(gitlab): add instance URL in screen name 2023-06-02 15:20:23 +02:00
Ali BARIN
ae316f60e4 fix: stop checking auth urls 2023-06-02 15:20:09 +02:00
Rıdvan Akca
1ac423ba56 chore: update action and trigger names 2023-06-01 15:01:12 +02:00
Ali BARIN
b43490dd76 fix(get-dynamic-fields): fetch variables field 2023-06-01 13:28:36 +02:00
Ali BARIN
f586e81dd1 fix(FlowSubstep): count 0 as value towards required check 2023-06-01 13:28:36 +02:00
Ali BARIN
8a6d8a7d8c fix(google-sheets/list-sheet-headers): early exit upon not existing worksheet 2023-06-01 13:28:36 +02:00
Ali BARIN
45d607f1a0 fix(google-sheets/list-sheet-headers): cover worksheetId 0 2023-06-01 13:28:36 +02:00
Ali BARIN
d84abaa229 chore(google-sheets/create-spreedsheet-row): update metadata 2023-06-01 13:28:36 +02:00
Rıdvan Akca
3fd1d4d9b3 feat(google-sheets): create spreadsheet row 2023-06-01 13:28:36 +02:00
Ali BARIN
078b8efb56 feat(rss/new-items-in-feed): incorporate attributes 2023-05-30 16:26:48 +02:00
Ömer Faruk Aydın
5066995f72 Merge pull request #1117 from automatisch/barinali-patch-1
chore(gitlab): add empty d.ts file
2023-05-30 15:37:13 +02:00
Ali BARIN
577fe3dba8 chore(gitlab): add empty d.ts file 2023-05-30 15:33:14 +02:00
Krzysztof Dukszta-Kwiatkowski
d96f4999bc post review fixes 2023-05-30 15:21:13 +02:00
Krzysztof Dukszta-Kwiatkowski
6e80ff4eb6 post review fixes 2023-05-30 15:21:13 +02:00
Krzysztof Dukszta-Kwiatkowski
3f8f022d48 feat: gitlab triggers integration 2023-05-30 15:21:13 +02:00
Ali BARIN
93a2e2151e fix: early exit upon failed actions 2023-05-27 21:16:24 +02:00
Ömer Faruk Aydın
663a1ed9d4 Merge pull request #1108 from automatisch/docs/filter
docs: Add filter connection and actions
2023-05-24 15:42:04 +02:00
Ali BARIN
4f46c55c85 feat: add LOG_LEVEL env. var. 2023-05-24 15:35:38 +02:00
Ali BARIN
9701c98af9 feat: error log errors in action and triggers 2023-05-24 15:35:38 +02:00
Ali BARIN
aabf2a1c79 feat: debug log incoming webhook requests 2023-05-24 15:35:38 +02:00
Ali BARIN
29539b090e feat: cover arrays in error handling 2023-05-24 15:35:38 +02:00
Faruk AYDIN
1c80677ac3 docs: Add filter connection and actions 2023-05-24 15:23:38 +02:00
Ali BARIN
ad419855e9 feat(webhook/handler): use UUID for internal ids 2023-05-24 12:59:48 +02:00
Ömer Faruk Aydın
30b75943f3 Merge pull request #1103 from automatisch/release/0.7.1
Release v0.7.1
2023-05-19 20:31:09 +02:00
Faruk AYDIN
41a67b402d Release v0.7.1 2023-05-19 19:56:51 +02:00
Faruk AYDIN
caa104b1cc chore: Update version to 0.7.1 in Dockerfiles 2023-05-19 19:56:01 +02:00
Rıdvan Akca
94085f2bc8 fix(shared-drive): add missing field to payload 2023-05-19 17:55:52 +02:00
Faruk AYDIN
d39c962314 docs: Add Google Sheets to available apps 2023-05-19 16:15:46 +02:00
Ömer Faruk Aydın
706fb0f063 Merge pull request #1100 from automatisch/release/0.7.0
Release v0.7.0
2023-05-19 13:16:42 +02:00
Faruk AYDIN
b9d89b040f Release v0.7.0 2023-05-19 12:29:29 +02:00
Faruk AYDIN
41421b849a chore: Update version to 0.7.0 in Dockerfiles 2023-05-19 12:28:59 +02:00
Rıdvan Akca
324375da93 fix(shared-drive): show shared drive items 2023-05-19 07:35:49 +02:00
Ali BARIN
536446faf6 fix(twilio/receive-sms): use phonenumber sid in removing webhook 2023-05-17 12:57:02 +02:00
Rıdvan Akca
d026ac09f3 feat(google-sheets): add new worksheets trigger 2023-05-16 23:42:23 +02:00
Ali BARIN
88c93ac992 fix(dynamic-data): correct parameters 2023-05-16 17:59:04 +02:00
Ali BARIN
d540322d8b refactor(webhook): respond with 204 instead of 200 2023-05-16 16:40:12 +02:00
Ali BARIN
ad4db5e936 feat(twilio/send-sms): use dynamic phone numbers 2023-05-16 16:40:12 +02:00
Ali BARIN
25cb4d90f3 refactor(twilio/receive-sms): convert to webhook 2023-05-16 16:40:12 +02:00
Ömer Faruk Aydın
6c14a353ef Merge pull request #1093 from automatisch/openai-chat-prompt
feat(openai): add chat prompt
2023-05-16 12:46:41 +02:00
Ali BARIN
74d7d1aa98 feat(openai): add chat prompt 2023-05-15 20:41:25 +00:00
Ali BARIN
43b0d9ed29 fix: refetch step executions upon deleting and testing steps 2023-05-15 16:24:26 +02:00
Ali BARIN
3572e6f65a fix: send parameters to dynamic data query 2023-05-15 16:24:17 +02:00
Ali BARIN
d23d5d2da0 feat(slack): send direct message 2023-05-15 16:24:17 +02:00
Faruk AYDIN
183b9b0d88 feat: Add enable ssl field to PostgreSQL connection 2023-05-15 16:23:43 +02:00
Faruk AYDIN
7a1af268ae chore: Remove empty line from PostgreSQL index file 2023-05-15 16:23:43 +02:00
Ali BARIN
f879b3c5b0 refactor(postgresql): rename pgClient with client 2023-05-15 16:23:43 +02:00
Ali BARIN
40be72cf65 feat(postgresql/delete): add interactive where clause entries 2023-05-15 16:23:43 +02:00
Ali BARIN
a8886571d1 feat(postgresql/update): add interactive where clause 2023-05-15 16:23:43 +02:00
Ali BARIN
1fcd51ea26 refactor(postgresql/insert): use withSchema 2023-05-15 16:23:43 +02:00
Ali BARIN
89752138be refactor(postgresql): use bindings to set run-time params 2023-05-15 16:23:43 +02:00
Ali BARIN
f29ccace2a chore(postgresql): rename app folder and add icon 2023-05-15 16:23:43 +02:00
Shehab Ghazy
0c8343e76f feat(postgresql): add auth and primitive actions 2023-05-15 16:23:43 +02:00
Ali BARIN
9776c9f5a4 feat: add duplicate flow functionality 2023-05-13 14:44:21 +02:00
Ali BARIN
a5dbac9817 feat(ExecutionStep): show execution date 2023-05-13 13:20:05 +02:00
Ali BARIN
bad5e0b855 fix(queries/get-execution): serve soft deleteds 2023-05-13 13:19:53 +02:00
Ali BARIN
8e4ca55560 feat(ControlledAutocomplete): filter by value too 2023-05-13 13:19:42 +02:00
Ali BARIN
f52afc1fe0 chore: serve graphql explorer only on development 2023-05-13 13:19:35 +02:00
Ali BARIN
815e64302e fix(Editor): don't unregister step parameters 2023-05-13 13:19:28 +02:00
Faruk AYDIN
07b2b18a4e fix: Run remove cancelled subscriptions only in the cloud 2023-05-12 13:30:06 +02:00
Ömer Faruk Aydın
69eca33de7 Merge pull request #1080 from automatisch/docs-postgres-ssl
docs: Add POSTGRES_ENABLE_SSL env variable to configuration
2023-05-11 14:50:32 +02:00
Faruk AYDIN
ec76a480d0 docs: Add POSTGRES_ENABLE_SSL env variable to configuration 2023-05-11 13:58:02 +02:00
Ali BARIN
a8823c3ed0 fix(filters/continue): cover multiple conditions 2023-05-09 18:28:51 +02:00
Ali BARIN
1f1b3a341c refactor: make sentry cloud agnostic 2023-05-09 13:17:33 +02:00
Ali BARIN
8c164a3852 fix(http-request): suppress failure upon size check 2023-05-08 14:45:41 +02:00
Ali BARIN
dcf526d810 feat(http-request): convert non-text data to base64 2023-05-08 12:52:09 +02:00
Ali BARIN
2fc6d680a0 fix: incorporate spaces in variables 2023-05-08 12:52:09 +02:00
Ali BARIN
f414972f33 Merge pull request #1074 from automatisch/executions-updated-at
feat: sort executions by updated at
2023-05-08 12:49:15 +02:00
Ali BARIN
69d192d989 feat: sort executions by updated at 2023-05-02 09:30:41 +00:00
Ömer Faruk Aydın
6c8769e598 Merge pull request #1071 from automatisch/paddle-plans
feat: Introduce new plans for the cloud
2023-04-26 13:21:18 +02:00
Faruk AYDIN
c12703422c feat: Introduce new plans for the cloud 2023-04-26 12:13:39 +02:00
Ömer Faruk Aydın
c0171e1cd1 Merge pull request #1070 from automatisch/dockerfile.cloud
chore: add dockerfile for cloud
2023-04-26 11:20:00 +02:00
Ali BARIN
920a983146 chore: add dockerfile for cloud 2023-04-25 20:43:48 +00:00
Ömer Faruk Aydın
7ec86bfef1 Merge pull request #1069 from automatisch/release/0.6.1
Release v0.6.1
2023-04-25 18:17:04 +02:00
304 changed files with 8345 additions and 934 deletions

8
.dockerignore Normal file
View File

@@ -0,0 +1,8 @@
**/node_modules/
**/dist/
**/logs/
**/.devcontainer
**/.github
**/.vscode
packages/docs
packages/e2e-test

3
.gitignore vendored
View File

@@ -125,3 +125,6 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# MacOS finder preferences
.DS_store

View File

@@ -4,7 +4,7 @@ WORKDIR /automatisch
RUN \
apk --no-cache add --virtual build-dependencies python3 build-base && \
yarn global add @automatisch/cli@0.6.1 --network-timeout 1000000 && \
yarn global add @automatisch/cli@0.8.0 --network-timeout 1000000 && \
rm -rf /usr/local/share/.cache/ && \
apk del build-dependencies

19
docker/Dockerfile.cloud Normal file
View File

@@ -0,0 +1,19 @@
# syntax=docker/dockerfile:1
FROM node:16-alpine
WORKDIR /automatisch
ENV PORT 3000
RUN ls -lna
# copy the app, note .dockerignore
COPY . ./
RUN yarn
RUN yarn lerna bootstrap
RUN yarn lerna run --scope=@*/{web,backend,cli} build
COPY ./docker/entrypoint-cloud.sh /entrypoint-cloud.sh
EXPOSE 3000
ENTRYPOINT ["sh", "/entrypoint-cloud.sh"]

View File

@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
FROM automatischio/automatisch:0.6.1
FROM automatischio/automatisch:0.8.0
WORKDIR /automatisch
RUN apk add --no-cache openssl dos2unix

9
docker/entrypoint-cloud.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
set -e
if [ -n "$WORKER" ]; then
yarn automatisch start-worker
else
yarn automatisch start
fi

View File

@@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "0.6.1",
"version": "0.8.0",
"npmClient": "yarn",
"useWorkspaces": true,
"command": {

View File

@@ -1,6 +1,6 @@
{
"name": "@automatisch/backend",
"version": "0.6.1",
"version": "0.8.0",
"license": "See LICENSE file",
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
"scripts": {
@@ -22,7 +22,7 @@
"prebuild": "rm -rf ./dist"
},
"dependencies": {
"@automatisch/web": "^0.6.1",
"@automatisch/web": "^0.8.0",
"@bull-board/express": "^3.10.1",
"@graphql-tools/graphql-file-loader": "^7.3.4",
"@graphql-tools/load": "^7.5.2",
@@ -30,6 +30,7 @@
"@sentry/node": "^7.42.0",
"@sentry/tracing": "^7.42.0",
"@types/luxon": "^2.3.1",
"@types/xmlrpc": "^1.3.7",
"ajv-formats": "^2.1.1",
"axios": "0.24.0",
"bcrypt": "^5.0.1",
@@ -49,6 +50,8 @@
"graphql-type-json": "^0.3.2",
"handlebars": "^4.7.7",
"http-errors": "~1.6.3",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.1",
"jsonwebtoken": "^9.0.0",
"knex": "^2.4.0",
"lodash.get": "^4.4.2",
@@ -62,7 +65,8 @@
"pg": "^8.7.1",
"php-serialize": "^4.0.2",
"stripe": "^11.13.0",
"winston": "^3.7.1"
"winston": "^3.7.1",
"xmlrpc": "^1.3.2"
},
"contributors": [
{
@@ -100,7 +104,7 @@
"url": "https://github.com/automatisch/automatisch/issues"
},
"devDependencies": {
"@automatisch/types": "^0.6.1",
"@automatisch/types": "^0.8.0",
"@types/bcrypt": "^5.0.0",
"@types/bull": "^3.15.8",
"@types/cors": "^2.8.12",

View File

@@ -2,7 +2,7 @@ import qs from 'qs';
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Translate Text',
name: 'Translate text',
key: 'translateText',
description: 'Translates text from one language to another.',
arguments: [
@@ -20,7 +20,7 @@ export default defineAction({
type: 'dropdown' as const,
required: true,
description: 'Language to translate the text to.',
variables: false,
variables: true,
value: '',
options: [
{ label: 'Bulgarian', value: 'BG' },

View File

@@ -1,7 +1,7 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Delay For',
name: 'Delay for',
key: 'delayFor',
description:
'Delays the execution of the next action by a specified amount of time.',
@@ -13,7 +13,7 @@ export default defineAction({
required: true,
value: null,
description: 'Delay for unit, e.g. minutes, hours, days, weeks.',
variables: false,
variables: true,
options: [
{
label: 'Minutes',

View File

@@ -1,7 +1,7 @@
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Delay Until',
name: 'Delay until',
key: 'delayUntil',
description:
'Delays the execution of the next action until a specified date.',

View File

@@ -11,7 +11,7 @@ export default defineAction({
type: 'dropdown' as const,
required: true,
description: 'Pick a channel to send the message to.',
variables: false,
variables: true,
source: {
type: 'query',
name: 'getDynamicData',

View File

@@ -19,8 +19,8 @@ export default {
channels.data = response.data
.filter((channel: IJSONObject) => {
// filter in text channels only
return channel.type === 0;
// filter in text channels and announcement channels only
return channel.type === 0 || channel.type === 5;
})
.map((channel: IJSONObject) => {
return {

View File

@@ -10,7 +10,7 @@ type TGroupItem = {
type TGroup = Record<'and', TGroupItem[]>;
const isEqual = (a: string, b: string) => a === b;
const isNotEqual = (a: string, b: string) => !isEqual(a, b)
const isNotEqual = (a: string, b: string) => !isEqual(a, b);
const isGreaterThan = (a: string, b: string) => Number(a) > Number(b);
const isLessThan = (a: string, b: string) => Number(a) < Number(b);
const isGreaterThanOrEqual = (a: string, b: string) => Number(a) >= Number(b);
@@ -18,6 +18,36 @@ const isLessThanOrEqual = (a: string, b: string) => Number(a) <= Number(b);
const contains = (a: string, b: string) => a.includes(b);
const doesNotContain = (a: string, b: string) => !contains(a, b);
const shouldContinue = (orGroups: TGroup[]) => {
let atLeastOneGroupMatches = false;
for (const group of orGroups) {
let groupMatches = true;
for (const condition of group.and) {
const conditionMatches = operate(
condition.operator,
condition.key,
condition.value
);
if (!conditionMatches) {
groupMatches = false;
break;
}
}
if (groupMatches) {
atLeastOneGroupMatches = true;
break;
}
}
return atLeastOneGroupMatches;
}
type TOperatorFunc = (a: string, b: string) => boolean;
type TOperators = {
@@ -66,7 +96,7 @@ export default defineAction({
return groups;
}, []);
if (matchingGroups.length === 0) {
if (!shouldContinue(orGroups)) {
$.execution.exit();
}

View File

@@ -20,12 +20,14 @@ export default defineTrigger({
],
async testRun($) {
if (!isEmpty($.lastExecutionStep?.dataOut)) {
const lastExecutionStep = await $.getLastExecutionStep();
if (!isEmpty(lastExecutionStep?.dataOut)) {
$.pushTriggerItem({
raw: $.lastExecutionStep.dataOut,
raw: lastExecutionStep.dataOut,
meta: {
internalId: '',
}
},
});
}
},
@@ -35,20 +37,15 @@ export default defineTrigger({
name: $.flow.id,
type: 'POST',
url: $.webhookUrl,
filters: [$.step.parameters.filters]
filters: [$.step.parameters.filters],
};
const { data } = await $.http.post(
`/v2/public/api/webhooks`,
payload
);
const { data } = await $.http.post(`/v2/public/api/webhooks`, payload);
await $.flow.setRemoteWebhookId(data.id);
},
async unregisterHook($) {
await $.http.delete(
`/v2/public/api/webhooks/${$.flow.remoteWebhookId}`
);
await $.http.delete(`/v2/public/api/webhooks/${$.flow.remoteWebhookId}`);
},
});

View File

@@ -11,7 +11,7 @@ export default defineAction({
key: 'repo',
type: 'dropdown' as const,
required: false,
variables: false,
variables: true,
source: {
type: 'query',
name: 'getDynamicData',

View File

@@ -6,7 +6,7 @@ import actions from './actions';
import dynamicData from './dynamic-data';
export default defineApp({
name: 'Github',
name: 'GitHub',
key: 'github',
baseUrl: 'https://github.com',
apiBaseUrl: 'https://api.github.com',

View File

@@ -0,0 +1,2 @@
<!-- https://about.gitlab.com/images/press/logo/svg/gitlab-logo-500.svg -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380"><defs><style>.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}</style></defs><g id="LOGO"><path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-2" d="M282.83,170.73l-.27-.69a88.3,88.3,0,0,0-35.15,15.8L190,229.25c19.55,14.79,36.57,27.64,36.57,27.64l40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-3" d="M153.43,256.89l19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91S209.55,244,190,229.25C170.45,244,153.43,256.89,153.43,256.89Z"/><path class="cls-2" d="M132.58,185.84A88.19,88.19,0,0,0,97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82s17-12.85,36.57-27.64Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,24 @@
import { IGlobalVariable } from '@automatisch/types';
import { URL, URLSearchParams } from 'url';
import getBaseUrl from '../common/get-base-url';
export default async function generateAuthUrl($: IGlobalVariable) {
// ref: https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
const scopes = ['api', 'read_user'];
const searchParams = new URLSearchParams({
client_id: $.auth.data.clientId as string,
redirect_uri: $.auth.data.oAuthRedirectUrl as string,
scope: scopes.join(' '),
response_type: 'code',
state: Date.now().toString(),
});
const baseUrl = getBaseUrl($);
const path = `/oauth/authorize?${searchParams.toString()}`;
await $.auth.set({
url: new URL(path, baseUrl).toString(),
});
}

View File

@@ -0,0 +1,63 @@
import generateAuthUrl from './generate-auth-url';
import verifyCredentials from './verify-credentials';
import isStillVerified from './is-still-verified';
import refreshToken from './refresh-token';
export default {
fields: [
{
key: 'oAuthRedirectUrl',
label: 'OAuth Redirect URL',
type: 'string' as const,
required: true,
readOnly: true,
value: '{WEB_APP_URL}/app/gitlab/connections/add',
placeholder: null,
description:
'When asked to input an OAuth callback or redirect URL in Gitlab OAuth, enter the URL above.',
docUrl: 'https://automatisch.io/docs/gitlab#oauth-redirect-url',
clickToCopy: true,
},
{
key: 'instanceUrl',
label: 'Gitlab instance URL',
type: 'string' as const,
required: false,
readOnly: false,
value: 'https://gitlab.com',
placeholder: 'https://gitlab.com',
description: 'Your Gitlab instance URL. Default is https://gitlab.com.',
docUrl: 'https://automatisch.io/docs/gitlab#oauth-redirect-url',
clickToCopy: true,
},
{
key: 'clientId',
label: 'Client ID',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
docUrl: 'https://automatisch.io/docs/gitlab#client-id',
clickToCopy: false,
},
{
key: 'clientSecret',
label: 'Client Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
docUrl: 'https://automatisch.io/docs/gitlab#client-secret',
clickToCopy: false,
},
],
generateAuthUrl,
refreshToken,
verifyCredentials,
isStillVerified,
};

View File

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

View File

@@ -0,0 +1,24 @@
import { IGlobalVariable } from '@automatisch/types';
import { URLSearchParams } from 'url';
const refreshToken = async ($: IGlobalVariable) => {
// ref: https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
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('/oauth/token', params.toString());
await $.auth.set({
accessToken: data.access_token,
expiresIn: data.expires_in,
tokenType: data.token_type,
refreshToken: data.refresh_token,
});
};
export default refreshToken;

View File

@@ -0,0 +1,41 @@
import { IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const verifyCredentials = async ($: IGlobalVariable) => {
// ref: https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
const response = await $.http.post(
'/oauth/token',
{
client_id: $.auth.data.clientId,
client_secret: $.auth.data.clientSecret,
code: $.auth.data.code,
grant_type: 'authorization_code',
redirect_uri: $.auth.data.oAuthRedirectUrl,
},
{
headers: {
Accept: 'application/json',
},
}
);
const data = response.data;
$.auth.data.accessToken = data.access_token;
const currentUser = await getCurrentUser($);
await $.auth.set({
clientId: $.auth.data.clientId,
clientSecret: $.auth.data.clientSecret,
accessToken: data.access_token,
refreshToken: data.refresh_token,
scope: data.scope,
tokenType: data.token_type,
userId: currentUser.id,
screenName: `${currentUser.username} @ ${$.auth.data.instanceUrl}`,
});
};
export default verifyCredentials;

View File

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

View File

@@ -0,0 +1,15 @@
import { IGlobalVariable } from '@automatisch/types';
const getBaseUrl = ($: IGlobalVariable): string => {
if ($.auth.data.instanceUrl) {
return $.auth.data.instanceUrl as string;
}
if ($.app.apiBaseUrl) {
return $.app.apiBaseUrl;
}
return $.app.baseUrl;
};
export default getBaseUrl;

View File

@@ -0,0 +1,11 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
const getCurrentUser = async ($: IGlobalVariable): Promise<IJSONObject> => {
// ref: https://docs.gitlab.com/ee/api/users.html#list-current-user
const response = await $.http.get('/api/v4/user');
const currentUser = response.data;
return currentUser;
};
export default getCurrentUser;

View File

@@ -0,0 +1,33 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
import type { AxiosResponse } from 'axios';
import parseLinkHeader from '../../../helpers/parse-header-link';
type TResponse = {
data: IJSONObject[];
error?: IJSONObject;
};
export default async function paginateAll(
$: IGlobalVariable,
request: Promise<AxiosResponse>
) {
const response = await request;
const aggregatedResponse: TResponse = {
data: [...response.data],
};
let links = parseLinkHeader(response.headers.link);
while (links.next) {
const nextPageResponse = await $.http.request({
...response.config,
url: links.next.uri,
});
aggregatedResponse.data.push(...nextPageResponse.data);
links = parseLinkHeader(nextPageResponse.headers.link);
}
return aggregatedResponse;
}

View File

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

View File

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

View File

@@ -0,0 +1,33 @@
import { IGlobalVariable } from '@automatisch/types';
import paginateAll from '../../common/paginate-all';
export default {
name: 'List projects',
key: 'listProjects',
async run($: IGlobalVariable) {
// ref:
// - https://docs.gitlab.com/ee/api/projects.html#list-all-projects
// - https://docs.gitlab.com/ee/api/rest/index.html#keyset-based-pagination
const firstPageRequest = $.http.get('/api/v4/projects', {
params: {
simple: true,
pagination: 'keyset',
membership: true,
order_by: 'id',
sort: 'asc',
},
});
const response = await paginateAll($, firstPageRequest);
response.data = response.data.map((repo: { name: string; id: number }) => {
return {
value: repo.id,
name: repo.name,
};
});
return response;
},
};

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,21 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import setBaseUrl from './common/set-base-url';
import auth from './auth';
import triggers from './triggers';
import dynamicData from './dynamic-data';
export default defineApp({
name: 'GitLab',
key: 'gitlab',
baseUrl: 'https://gitlab.com',
apiBaseUrl: 'https://gitlab.com',
iconUrl: '{BASE_URL}/apps/gitlab/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/gitlab/connection',
primaryColor: 'FC6D26',
supportsConnections: true,
beforeRequest: [setBaseUrl, addAuthHeader],
auth,
triggers,
dynamicData,
});

View File

@@ -0,0 +1,27 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
// confidential_issues_events has the same event data as issues_events
import data from './issue_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Confidential issue event',
description:
'Confidential issue event (triggered when a new confidential issue is created or an existing issue is updated, closed, or reopened)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#issue-events',
key: GITLAB_EVENT_TYPE.confidential_issues_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.confidential_issues_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,159 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#issue-events
export default {
object_kind: 'issue',
event_type: 'issue',
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
email: 'admin@example.com',
},
project: {
id: 1,
name: 'Gitlab Test',
description: 'Aut reprehenderit ut est.',
web_url: 'http://example.com/gitlabhq/gitlab-test',
avatar_url: null,
git_ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
git_http_url: 'http://example.com/gitlabhq/gitlab-test.git',
namespace: 'GitlabHQ',
visibility_level: 20,
path_with_namespace: 'gitlabhq/gitlab-test',
default_branch: 'master',
ci_config_path: null,
homepage: 'http://example.com/gitlabhq/gitlab-test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
http_url: 'http://example.com/gitlabhq/gitlab-test.git',
},
object_attributes: {
id: 301,
title: 'New API: create/update/delete file',
assignee_ids: [51],
assignee_id: 51,
author_id: 51,
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
updated_by_id: 1,
last_edited_at: null,
last_edited_by_id: null,
relative_position: 0,
description: 'Create new API for manipulations with repository',
milestone_id: null,
state_id: 1,
confidential: false,
discussion_locked: true,
due_date: null,
moved_to_id: null,
duplicated_to_id: null,
time_estimate: 0,
total_time_spent: 0,
time_change: 0,
human_total_time_spent: null,
human_time_estimate: null,
human_time_change: null,
weight: null,
iid: 23,
url: 'http://example.com/diaspora/issues/23',
state: 'opened',
action: 'open',
severity: 'high',
escalation_status: 'triggered',
escalation_policy: {
id: 18,
name: 'Engineering On-call',
},
labels: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
},
repository: {
name: 'Gitlab Test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
description: 'Aut reprehenderit ut est.',
homepage: 'http://example.com/gitlabhq/gitlab-test',
},
assignees: [
{
name: 'User1',
username: 'user1',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
},
],
assignee: {
name: 'User1',
username: 'user1',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
},
labels: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
changes: {
updated_by_id: {
previous: null,
current: 1,
},
updated_at: {
previous: '2017-09-15 16:50:55 UTC',
current: '2017-09-15 16:52:00 UTC',
},
labels: {
previous: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
current: [
{
id: 205,
title: 'Platform',
color: '#123123',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'Platform related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
},
},
};

View File

@@ -0,0 +1,27 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
// confidential_note_events has the same event data as note_events
import data from './note_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Confidential comment event',
description:
'Confidential comment event (triggered when a new confidential comment is made on commits, merge requests, issues, and code snippets)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-events',
key: GITLAB_EVENT_TYPE.confidential_note_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.confidential_note_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,74 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-events
export default {
object_kind: 'note',
event_type: 'note',
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
email: 'admin@example.com',
},
project_id: 5,
project: {
id: 5,
name: 'Gitlab Test',
description: 'Aut reprehenderit ut est.',
web_url: 'http://example.com/gitlabhq/gitlab-test',
avatar_url: null,
git_ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
git_http_url: 'http://example.com/gitlabhq/gitlab-test.git',
namespace: 'GitlabHQ',
visibility_level: 20,
path_with_namespace: 'gitlabhq/gitlab-test',
default_branch: 'master',
homepage: 'http://example.com/gitlabhq/gitlab-test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
http_url: 'http://example.com/gitlabhq/gitlab-test.git',
},
repository: {
name: 'Gitlab Test',
url: 'http://example.com/gitlab-org/gitlab-test.git',
description: 'Aut reprehenderit ut est.',
homepage: 'http://example.com/gitlab-org/gitlab-test',
},
object_attributes: {
id: 1243,
note: 'This is a commit comment. How does this work?',
noteable_type: 'Commit',
author_id: 1,
created_at: '2015-05-17 18:08:09 UTC',
updated_at: '2015-05-17 18:08:09 UTC',
project_id: 5,
attachment: null,
line_code: 'bec9703f7a456cd2b4ab5fb3220ae016e3e394e3_0_1',
commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
noteable_id: null,
system: false,
st_diff: {
diff: '--- /dev/null\n+++ b/six\n@@ -0,0 +1 @@\n+Subproject commit 409f37c4f05865e4fb208c771485f211a22c4c2d\n',
new_path: 'six',
old_path: 'six',
a_mode: '0',
b_mode: '160000',
new_file: true,
renamed_file: false,
deleted_file: false,
},
url: 'http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660#note_1243',
},
commit: {
id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
message:
'Add submodule\n\nSigned-off-by: Example User \u003cuser@example.com.com\u003e\n',
timestamp: '2014-02-27T10:06:20+02:00',
url: 'http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
author: {
name: 'Example User',
email: 'user@example.com',
},
},
};

View File

@@ -0,0 +1,45 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#deployment-events
export default {
object_kind: 'deployment',
status: 'success',
status_changed_at: '2021-04-28 21:50:00 +0200',
deployment_id: 15,
deployable_id: 796,
deployable_url:
'http://10.126.0.2:3000/root/test-deployment-webhooks/-/jobs/796',
environment: 'staging',
environment_slug: 'staging',
environment_external_url: 'https://staging.example.com',
project: {
id: 30,
name: 'test-deployment-webhooks',
description: '',
web_url: 'http://10.126.0.2:3000/root/test-deployment-webhooks',
avatar_url: null,
git_ssh_url: 'ssh://vlad@10.126.0.2:2222/root/test-deployment-webhooks.git',
git_http_url: 'http://10.126.0.2:3000/root/test-deployment-webhooks.git',
namespace: 'Administrator',
visibility_level: 0,
path_with_namespace: 'root/test-deployment-webhooks',
default_branch: 'master',
ci_config_path: '',
homepage: 'http://10.126.0.2:3000/root/test-deployment-webhooks',
url: 'ssh://vlad@10.126.0.2:2222/root/test-deployment-webhooks.git',
ssh_url: 'ssh://vlad@10.126.0.2:2222/root/test-deployment-webhooks.git',
http_url: 'http://10.126.0.2:3000/root/test-deployment-webhooks.git',
},
short_sha: '279484c0',
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
email: 'admin@example.com',
},
user_url: 'http://10.126.0.2:3000/root',
commit_url:
'http://10.126.0.2:3000/root/test-deployment-webhooks/-/commit/279484c09fbe69ededfced8c1bb6e6d24616b468',
commit_title: 'Add new file',
};

View File

@@ -0,0 +1,26 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './deployment_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Deployment event',
description:
'Deployment event (triggered when a deployment starts, succeeds, fails or is canceled)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#deployment-events',
key: GITLAB_EVENT_TYPE.deployment_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.deployment_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,38 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#feature-flag-events
export default {
object_kind: 'feature_flag',
project: {
id: 1,
name: 'Gitlab Test',
description: 'Aut reprehenderit ut est.',
web_url: 'http://example.com/gitlabhq/gitlab-test',
avatar_url: null,
git_ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
git_http_url: 'http://example.com/gitlabhq/gitlab-test.git',
namespace: 'GitlabHQ',
visibility_level: 20,
path_with_namespace: 'gitlabhq/gitlab-test',
default_branch: 'master',
ci_config_path: null,
homepage: 'http://example.com/gitlabhq/gitlab-test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
http_url: 'http://example.com/gitlabhq/gitlab-test.git',
},
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
email: 'admin@example.com',
},
user_url: 'http://example.com/root',
object_attributes: {
id: 6,
name: 'test-feature-flag',
description: 'test-feature-flag-description',
active: true,
},
};

View File

@@ -0,0 +1,26 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './feature_flag_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Feature flag event',
description:
'Feature flag event (triggered when a feature flag is turned on or off)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#feature-flag-events',
key: GITLAB_EVENT_TYPE.feature_flag_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.feature_flag_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,29 @@
import confidentialIssueEvent from './confidential-issue-event';
import confidentialNoteEvent from './confidential-note-event';
import deploymentEvent from './deployment-event';
import featureFlagEvent from './feature-flag-event';
import issueEvent from './issue-event';
import jobEvent from './job-event';
import mergeRequestEvent from './merge-request-event';
import noteEvent from './note-event';
import pipelineEvent from './pipeline-event';
import pushEvent from './push-event';
import releaseEvent from './release-event';
import tagPushEvent from './tag-push-event';
import wikiPageEvent from './wiki-page-event';
export default [
confidentialIssueEvent,
confidentialNoteEvent,
deploymentEvent,
featureFlagEvent,
issueEvent,
jobEvent,
mergeRequestEvent,
noteEvent,
pipelineEvent,
pushEvent,
releaseEvent,
tagPushEvent,
wikiPageEvent,
];

View File

@@ -0,0 +1,26 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './issue_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Issue event',
description:
'Issue event (triggered when a new issue is created or an existing issue is updated, closed, or reopened)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#issue-events',
key: GITLAB_EVENT_TYPE.issues_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.issues_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,159 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#issue-events
export default {
object_kind: 'issue',
event_type: 'issue',
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
email: 'admin@example.com',
},
project: {
id: 1,
name: 'Gitlab Test',
description: 'Aut reprehenderit ut est.',
web_url: 'http://example.com/gitlabhq/gitlab-test',
avatar_url: null,
git_ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
git_http_url: 'http://example.com/gitlabhq/gitlab-test.git',
namespace: 'GitlabHQ',
visibility_level: 20,
path_with_namespace: 'gitlabhq/gitlab-test',
default_branch: 'master',
ci_config_path: null,
homepage: 'http://example.com/gitlabhq/gitlab-test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
http_url: 'http://example.com/gitlabhq/gitlab-test.git',
},
object_attributes: {
id: 301,
title: 'New API: create/update/delete file',
assignee_ids: [51],
assignee_id: 51,
author_id: 51,
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
updated_by_id: 1,
last_edited_at: null,
last_edited_by_id: null,
relative_position: 0,
description: 'Create new API for manipulations with repository',
milestone_id: null,
state_id: 1,
confidential: false,
discussion_locked: true,
due_date: null,
moved_to_id: null,
duplicated_to_id: null,
time_estimate: 0,
total_time_spent: 0,
time_change: 0,
human_total_time_spent: null,
human_time_estimate: null,
human_time_change: null,
weight: null,
iid: 23,
url: 'http://example.com/diaspora/issues/23',
state: 'opened',
action: 'open',
severity: 'high',
escalation_status: 'triggered',
escalation_policy: {
id: 18,
name: 'Engineering On-call',
},
labels: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
},
repository: {
name: 'Gitlab Test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
description: 'Aut reprehenderit ut est.',
homepage: 'http://example.com/gitlabhq/gitlab-test',
},
assignees: [
{
name: 'User1',
username: 'user1',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
},
],
assignee: {
name: 'User1',
username: 'user1',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
},
labels: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
changes: {
updated_by_id: {
previous: null,
current: 1,
},
updated_at: {
previous: '2017-09-15 16:50:55 UTC',
current: '2017-09-15 16:52:00 UTC',
},
labels: {
previous: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
current: [
{
id: 205,
title: 'Platform',
color: '#123123',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'Platform related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
},
},
};

View File

@@ -0,0 +1,25 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './job_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Job event',
description: 'Job event (triggered when the status of a job changes)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#job-events',
key: GITLAB_EVENT_TYPE.job_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.job_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,60 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#job-events
export default {
object_kind: 'build',
ref: 'gitlab-script-trigger',
tag: false,
before_sha: '2293ada6b400935a1378653304eaf6221e0fdb8f',
sha: '2293ada6b400935a1378653304eaf6221e0fdb8f',
build_id: 1977,
build_name: 'test',
build_stage: 'test',
build_status: 'created',
build_created_at: '2021-02-23T02:41:37.886Z',
build_started_at: null,
build_finished_at: null,
build_duration: null,
build_queued_duration: 1095.588715, // duration in seconds
build_allow_failure: false,
build_failure_reason: 'script_failure',
retries_count: 2, // the second retry of this job
pipeline_id: 2366,
project_id: 380,
project_name: 'gitlab-org/gitlab-test',
user: {
id: 3,
name: 'User',
email: 'user@gitlab.com',
avatar_url:
'http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon',
},
commit: {
id: 2366,
name: 'Build pipeline',
sha: '2293ada6b400935a1378653304eaf6221e0fdb8f',
message: 'test\n',
author_name: 'User',
author_email: 'user@gitlab.com',
status: 'created',
duration: null,
started_at: null,
finished_at: null,
},
repository: {
name: 'gitlab_test',
description: 'Atque in sunt eos similique dolores voluptatem.',
homepage: 'http://192.168.64.1:3005/gitlab-org/gitlab-test',
git_ssh_url: 'git@192.168.64.1:gitlab-org/gitlab-test.git',
git_http_url: 'http://192.168.64.1:3005/gitlab-org/gitlab-test.git',
visibility_level: 20,
},
runner: {
active: true,
runner_type: 'project_type',
is_shared: false,
id: 380987,
description: 'shared-runners-manager-6.gitlab.com',
tags: ['linux', 'docker'],
},
environment: null,
};

View File

@@ -0,0 +1,89 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
import Crypto from 'crypto';
import { GITLAB_EVENT_TYPE } from './types';
import appConfig from '../../../config/app';
export const projectArgumentDescriptor = {
label: 'Project',
key: 'projectId',
type: 'dropdown' as const,
required: true,
description: 'Pick a project to receive events from',
variables: false,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listProjects',
},
],
},
};
export const getTestRunFn =
(eventData: IJSONObject) => ($: IGlobalVariable) => {
/*
Not fetching actual events from gitlab and using static event data from documentation
as there is no way to filter out events of one category using gitlab event types,
filtering is very limited and uses different grouping than what is applicable when creating a webhook.
ref:
- https://docs.gitlab.com/ee/api/events.html#target-types
- https://docs.gitlab.com/ee/api/projects.html#add-project-hook
*/
if (!eventData) {
return;
}
const dataItem = {
raw: eventData,
meta: {
// there is no distinct id on gitlab event object thus creating it
internalId: Crypto.randomUUID(),
},
};
$.pushTriggerItem(dataItem);
return Promise.resolve();
};
export const getRegisterHookFn =
(eventType: GITLAB_EVENT_TYPE) => async ($: IGlobalVariable) => {
// ref: https://docs.gitlab.com/ee/api/projects.html#add-project-hook
const subscriptionPayload = {
url: $.webhookUrl,
token: appConfig.webhookSecretKey,
enable_ssl_verification: true,
[eventType]: true,
};
if (
['wildcard', 'regex'].includes(
$.step.parameters.branch_filter_strategy as string
)
) {
subscriptionPayload.branch_filter_strategy = $.step.parameters
.branch_filter_strategy as string;
subscriptionPayload.push_events_branch_filter = $.step.parameters
.push_events_branch_filter as string;
}
const { data } = await $.http.post(
`/api/v4/projects/${$.step.parameters.projectId}/hooks`,
subscriptionPayload
);
await $.flow.setRemoteWebhookId(data.id.toString());
};
export const unregisterHook = async ($: IGlobalVariable) => {
// ref: https://docs.gitlab.com/ee/api/projects.html#delete-project-hook
await $.http.delete(
`/api/v4/projects/${$.step.parameters.projectId}/hooks/${$.flow.remoteWebhookId}`
);
};

View File

@@ -0,0 +1,26 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './merge_request_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Merge request event',
description:
'Merge request event (triggered when merge request is created, updated, or closed)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#merge-request-events',
key: GITLAB_EVENT_TYPE.merge_requests_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.merge_requests_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,208 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#merge-request-events
export default {
object_kind: 'merge_request',
event_type: 'merge_request',
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
email: 'admin@example.com',
},
project: {
id: 1,
name: 'Gitlab Test',
description: 'Aut reprehenderit ut est.',
web_url: 'http://example.com/gitlabhq/gitlab-test',
avatar_url: null,
git_ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
git_http_url: 'http://example.com/gitlabhq/gitlab-test.git',
namespace: 'GitlabHQ',
visibility_level: 20,
path_with_namespace: 'gitlabhq/gitlab-test',
default_branch: 'master',
ci_config_path: '',
homepage: 'http://example.com/gitlabhq/gitlab-test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
http_url: 'http://example.com/gitlabhq/gitlab-test.git',
},
repository: {
name: 'Gitlab Test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
description: 'Aut reprehenderit ut est.',
homepage: 'http://example.com/gitlabhq/gitlab-test',
},
object_attributes: {
id: 99,
iid: 1,
target_branch: 'master',
source_branch: 'ms-viewport',
source_project_id: 14,
author_id: 51,
assignee_ids: [6],
assignee_id: 6,
reviewer_ids: [6],
title: 'MS-Viewport',
created_at: '2013-12-03T17:23:34Z',
updated_at: '2013-12-03T17:23:34Z',
last_edited_at: '2013-12-03T17:23:34Z',
last_edited_by_id: 1,
milestone_id: null,
state_id: 1,
state: 'opened',
blocking_discussions_resolved: true,
work_in_progress: false,
first_contribution: true,
merge_status: 'unchecked',
target_project_id: 14,
description: '',
total_time_spent: 1800,
time_change: 30,
human_total_time_spent: '30m',
human_time_change: '30s',
human_time_estimate: '30m',
url: 'http://example.com/diaspora/merge_requests/1',
source: {
name: 'Awesome Project',
description: 'Aut reprehenderit ut est.',
web_url: 'http://example.com/awesome_space/awesome_project',
avatar_url: null,
git_ssh_url: 'git@example.com:awesome_space/awesome_project.git',
git_http_url: 'http://example.com/awesome_space/awesome_project.git',
namespace: 'Awesome Space',
visibility_level: 20,
path_with_namespace: 'awesome_space/awesome_project',
default_branch: 'master',
homepage: 'http://example.com/awesome_space/awesome_project',
url: 'http://example.com/awesome_space/awesome_project.git',
ssh_url: 'git@example.com:awesome_space/awesome_project.git',
http_url: 'http://example.com/awesome_space/awesome_project.git',
},
target: {
name: 'Awesome Project',
description: 'Aut reprehenderit ut est.',
web_url: 'http://example.com/awesome_space/awesome_project',
avatar_url: null,
git_ssh_url: 'git@example.com:awesome_space/awesome_project.git',
git_http_url: 'http://example.com/awesome_space/awesome_project.git',
namespace: 'Awesome Space',
visibility_level: 20,
path_with_namespace: 'awesome_space/awesome_project',
default_branch: 'master',
homepage: 'http://example.com/awesome_space/awesome_project',
url: 'http://example.com/awesome_space/awesome_project.git',
ssh_url: 'git@example.com:awesome_space/awesome_project.git',
http_url: 'http://example.com/awesome_space/awesome_project.git',
},
last_commit: {
id: 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7',
message: 'fixed readme',
title: 'Update file README.md',
timestamp: '2012-01-03T23:36:29+02:00',
url: 'http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7',
author: {
name: 'GitLab dev user',
email: 'gitlabdev@dv6700.(none)',
},
},
labels: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
action: 'open',
detailed_merge_status: 'mergeable',
},
labels: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
changes: {
updated_by_id: {
previous: null,
current: 1,
},
updated_at: {
previous: '2017-09-15 16:50:55 UTC',
current: '2017-09-15 16:52:00 UTC',
},
labels: {
previous: [
{
id: 206,
title: 'API',
color: '#ffffff',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'API related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
current: [
{
id: 205,
title: 'Platform',
color: '#123123',
project_id: 14,
created_at: '2013-12-03T17:15:43Z',
updated_at: '2013-12-03T17:15:43Z',
template: false,
description: 'Platform related issues',
type: 'ProjectLabel',
group_id: 41,
},
],
},
last_edited_at: {
previous: null,
current: '2023-03-15 00:00:10 UTC',
},
last_edited_by_id: {
previous: null,
current: 3278533,
},
},
assignees: [
{
id: 6,
name: 'User1',
username: 'user1',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
},
],
reviewers: [
{
id: 6,
name: 'User1',
username: 'user1',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
},
],
};

View File

@@ -0,0 +1,26 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './note_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Comment event',
description:
'Comment event (triggered when a new comment is made on commits, merge requests, issues, and code snippets)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-events',
key: GITLAB_EVENT_TYPE.note_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.note_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,74 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-events
export default {
object_kind: 'note',
event_type: 'note',
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon',
email: 'admin@example.com',
},
project_id: 5,
project: {
id: 5,
name: 'Gitlab Test',
description: 'Aut reprehenderit ut est.',
web_url: 'http://example.com/gitlabhq/gitlab-test',
avatar_url: null,
git_ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
git_http_url: 'http://example.com/gitlabhq/gitlab-test.git',
namespace: 'GitlabHQ',
visibility_level: 20,
path_with_namespace: 'gitlabhq/gitlab-test',
default_branch: 'master',
homepage: 'http://example.com/gitlabhq/gitlab-test',
url: 'http://example.com/gitlabhq/gitlab-test.git',
ssh_url: 'git@example.com:gitlabhq/gitlab-test.git',
http_url: 'http://example.com/gitlabhq/gitlab-test.git',
},
repository: {
name: 'Gitlab Test',
url: 'http://example.com/gitlab-org/gitlab-test.git',
description: 'Aut reprehenderit ut est.',
homepage: 'http://example.com/gitlab-org/gitlab-test',
},
object_attributes: {
id: 1243,
note: 'This is a commit comment. How does this work?',
noteable_type: 'Commit',
author_id: 1,
created_at: '2015-05-17 18:08:09 UTC',
updated_at: '2015-05-17 18:08:09 UTC',
project_id: 5,
attachment: null,
line_code: 'bec9703f7a456cd2b4ab5fb3220ae016e3e394e3_0_1',
commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
noteable_id: null,
system: false,
st_diff: {
diff: '--- /dev/null\n+++ b/six\n@@ -0,0 +1 @@\n+Subproject commit 409f37c4f05865e4fb208c771485f211a22c4c2d\n',
new_path: 'six',
old_path: 'six',
a_mode: '0',
b_mode: '160000',
new_file: true,
renamed_file: false,
deleted_file: false,
},
url: 'http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660#note_1243',
},
commit: {
id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
message:
'Add submodule\n\nSigned-off-by: Example User \u003cuser@example.com.com\u003e\n',
timestamp: '2014-02-27T10:06:20+02:00',
url: 'http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
author: {
name: 'Example User',
email: 'user@example.com',
},
},
};

View File

@@ -0,0 +1,26 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './pipeline_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Pipeline event',
description:
'Pipeline event (triggered when the status of a pipeline changes)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#pipeline-events',
key: GITLAB_EVENT_TYPE.pipeline_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.pipeline_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,254 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#pipeline-events
export default {
object_kind: 'pipeline',
object_attributes: {
id: 31,
iid: 3,
ref: 'master',
tag: false,
sha: 'bcbb5ec396a2c0f828686f14fac9b80b780504f2',
before_sha: 'bcbb5ec396a2c0f828686f14fac9b80b780504f2',
source: 'merge_request_event',
status: 'success',
stages: ['build', 'test', 'deploy'],
created_at: '2016-08-12 15:23:28 UTC',
finished_at: '2016-08-12 15:26:29 UTC',
duration: 63,
variables: [
{
key: 'NESTOR_PROD_ENVIRONMENT',
value: 'us-west-1',
},
],
},
merge_request: {
id: 1,
iid: 1,
title: 'Test',
source_branch: 'test',
source_project_id: 1,
target_branch: 'master',
target_project_id: 1,
state: 'opened',
merge_status: 'can_be_merged',
detailed_merge_status: 'mergeable',
url: 'http://192.168.64.1:3005/gitlab-org/gitlab-test/merge_requests/1',
},
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon',
email: 'user_email@gitlab.com',
},
project: {
id: 1,
name: 'Gitlab Test',
description: 'Atque in sunt eos similique dolores voluptatem.',
web_url: 'http://192.168.64.1:3005/gitlab-org/gitlab-test',
avatar_url: null,
git_ssh_url: 'git@192.168.64.1:gitlab-org/gitlab-test.git',
git_http_url: 'http://192.168.64.1:3005/gitlab-org/gitlab-test.git',
namespace: 'Gitlab Org',
visibility_level: 20,
path_with_namespace: 'gitlab-org/gitlab-test',
default_branch: 'master',
},
commit: {
id: 'bcbb5ec396a2c0f828686f14fac9b80b780504f2',
message: 'test\n',
timestamp: '2016-08-12T17:23:21+02:00',
url: 'http://example.com/gitlab-org/gitlab-test/commit/bcbb5ec396a2c0f828686f14fac9b80b780504f2',
author: {
name: 'User',
email: 'user@gitlab.com',
},
},
source_pipeline: {
project: {
id: 41,
web_url: 'https://gitlab.example.com/gitlab-org/upstream-project',
path_with_namespace: 'gitlab-org/upstream-project',
},
pipeline_id: 30,
job_id: 3401,
},
builds: [
{
id: 380,
stage: 'deploy',
name: 'production',
status: 'skipped',
created_at: '2016-08-12 15:23:28 UTC',
started_at: null,
finished_at: null,
duration: null,
queued_duration: null,
failure_reason: null,
when: 'manual',
manual: true,
allow_failure: false,
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon',
email: 'admin@example.com',
},
runner: null,
artifacts_file: {
filename: null,
size: null,
},
environment: {
name: 'production',
action: 'start',
deployment_tier: 'production',
},
},
{
id: 377,
stage: 'test',
name: 'test-image',
status: 'success',
created_at: '2016-08-12 15:23:28 UTC',
started_at: '2016-08-12 15:26:12 UTC',
finished_at: '2016-08-12 15:26:29 UTC',
duration: 17.0,
queued_duration: 196.0,
failure_reason: null,
when: 'on_success',
manual: false,
allow_failure: false,
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon',
email: 'admin@example.com',
},
runner: {
id: 380987,
description: 'shared-runners-manager-6.gitlab.com',
active: true,
runner_type: 'instance_type',
is_shared: true,
tags: ['linux', 'docker', 'shared-runner'],
},
artifacts_file: {
filename: null,
size: null,
},
environment: null,
},
{
id: 378,
stage: 'test',
name: 'test-build',
status: 'failed',
created_at: '2016-08-12 15:23:28 UTC',
started_at: '2016-08-12 15:26:12 UTC',
finished_at: '2016-08-12 15:26:29 UTC',
duration: 17.0,
queued_duration: 196.0,
failure_reason: 'script_failure',
when: 'on_success',
manual: false,
allow_failure: false,
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon',
email: 'admin@example.com',
},
runner: {
id: 380987,
description: 'shared-runners-manager-6.gitlab.com',
active: true,
runner_type: 'instance_type',
is_shared: true,
tags: ['linux', 'docker'],
},
artifacts_file: {
filename: null,
size: null,
},
environment: null,
},
{
id: 376,
stage: 'build',
name: 'build-image',
status: 'success',
created_at: '2016-08-12 15:23:28 UTC',
started_at: '2016-08-12 15:24:56 UTC',
finished_at: '2016-08-12 15:25:26 UTC',
duration: 17.0,
queued_duration: 196.0,
failure_reason: null,
when: 'on_success',
manual: false,
allow_failure: false,
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon',
email: 'admin@example.com',
},
runner: {
id: 380987,
description: 'shared-runners-manager-6.gitlab.com',
active: true,
runner_type: 'instance_type',
is_shared: true,
tags: ['linux', 'docker'],
},
artifacts_file: {
filename: null,
size: null,
},
environment: null,
},
{
id: 379,
stage: 'deploy',
name: 'staging',
status: 'created',
created_at: '2016-08-12 15:23:28 UTC',
started_at: null,
finished_at: null,
duration: null,
queued_duration: null,
failure_reason: null,
when: 'on_success',
manual: false,
allow_failure: false,
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon',
email: 'admin@example.com',
},
runner: null,
artifacts_file: {
filename: null,
size: null,
},
environment: {
name: 'staging',
action: 'start',
deployment_tier: 'staging',
},
},
],
};

View File

@@ -0,0 +1,62 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './push_event';
export const branchFilterStrategyArgumentDescriptor = {
label: 'What type of filter to use?',
key: 'branch_filter_strategy',
type: 'dropdown' as const,
description: 'Defaults to including all branches',
required: true,
variables: false,
value: 'all_branches',
options: [
{
label: 'All branches',
value: 'all_branches',
},
{
label: 'Wildcard pattern (ex: *-stable)',
value: 'wildcard',
},
{
label: 'Regular expression (ex: ^(feature|hotfix)/)',
value: 'regex',
},
],
};
export const pushEventsBranchFilterArgumentDescriptor = {
label: 'Filter value',
key: 'push_events_branch_filter',
description: 'Leave empty when using "all branches"',
type: 'string' as const,
required: false,
variables: false,
};
export const triggerDescriptor: IRawTrigger = {
name: 'Push event',
description: 'Push event (triggered when you push to the repository)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#push-events',
key: GITLAB_EVENT_TYPE.push_events,
type: 'webhook',
arguments: [
projectArgumentDescriptor,
branchFilterStrategyArgumentDescriptor,
pushEventsBranchFilterArgumentDescriptor,
],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.push_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,75 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#push-events
export default {
object_kind: 'push',
event_name: 'push',
before: '95790bf891e76fee5e1747ab589903a6a1f80f22',
after: 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7',
ref: 'refs/heads/master',
checkout_sha: 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7',
user_id: 4,
user_name: 'John Smith',
user_username: 'jsmith',
user_email: 'john@example.com',
user_avatar:
'https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80',
project_id: 15,
project: {
id: 15,
name: 'Diaspora',
description: '',
web_url: 'http://example.com/mike/diaspora',
avatar_url: null,
git_ssh_url: 'git@example.com:mike/diaspora.git',
git_http_url: 'http://example.com/mike/diaspora.git',
namespace: 'Mike',
visibility_level: 0,
path_with_namespace: 'mike/diaspora',
default_branch: 'master',
homepage: 'http://example.com/mike/diaspora',
url: 'git@example.com:mike/diaspora.git',
ssh_url: 'git@example.com:mike/diaspora.git',
http_url: 'http://example.com/mike/diaspora.git',
},
repository: {
name: 'Diaspora',
url: 'git@example.com:mike/diaspora.git',
description: '',
homepage: 'http://example.com/mike/diaspora',
git_http_url: 'http://example.com/mike/diaspora.git',
git_ssh_url: 'git@example.com:mike/diaspora.git',
visibility_level: 0,
},
commits: [
{
id: 'b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327',
message:
'Update Catalan translation to e38cb41.\n\nSee https://gitlab.com/gitlab-org/gitlab for more information',
title: 'Update Catalan translation to e38cb41.',
timestamp: '2011-12-12T14:27:31+02:00',
url: 'http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327',
author: {
name: 'Jordi Mallach',
email: 'jordi@softcatala.org',
},
added: ['CHANGELOG'],
modified: ['app/controller/application.rb'],
removed: [],
},
{
id: 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7',
message: 'fixed readme',
title: 'fixed readme',
timestamp: '2012-01-03T23:36:29+02:00',
url: 'http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7',
author: {
name: 'GitLab dev user',
email: 'gitlabdev@dv6700.(none)',
},
added: ['CHANGELOG'],
modified: ['app/controller/application.rb'],
removed: [],
},
],
total_commits_count: 4,
};

View File

@@ -0,0 +1,25 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './release_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Release event',
description: 'Release event (triggered when a release is created or updated)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#release-events',
key: GITLAB_EVENT_TYPE.releases_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.releases_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,72 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#release-events
export default {
object_kind: 'release',
id: 1,
created_at: '2020-11-02 12:55:12 UTC',
description: 'v1.1 has been released',
name: 'v1.1',
released_at: '2020-11-02 12:55:12 UTC',
tag: 'v1.1',
project: {
id: 2,
name: 'release-webhook-example',
description: '',
web_url: 'https://example.com/gitlab-org/release-webhook-example',
avatar_url: null,
git_ssh_url: 'ssh://git@example.com/gitlab-org/release-webhook-example.git',
git_http_url: 'https://example.com/gitlab-org/release-webhook-example.git',
namespace: 'Gitlab',
visibility_level: 0,
path_with_namespace: 'gitlab-org/release-webhook-example',
default_branch: 'master',
ci_config_path: null,
homepage: 'https://example.com/gitlab-org/release-webhook-example',
url: 'ssh://git@example.com/gitlab-org/release-webhook-example.git',
ssh_url: 'ssh://git@example.com/gitlab-org/release-webhook-example.git',
http_url: 'https://example.com/gitlab-org/release-webhook-example.git',
},
url: 'https://example.com/gitlab-org/release-webhook-example/-/releases/v1.1',
action: 'create',
assets: {
count: 5,
links: [
{
id: 1,
external: true, // deprecated in GitLab 15.9, will be removed in GitLab 16.0.
link_type: 'other',
name: 'Changelog',
url: 'https://example.net/changelog',
},
],
sources: [
{
format: 'zip',
url: 'https://example.com/gitlab-org/release-webhook-example/-/archive/v1.1/release-webhook-example-v1.1.zip',
},
{
format: 'tar.gz',
url: 'https://example.com/gitlab-org/release-webhook-example/-/archive/v1.1/release-webhook-example-v1.1.tar.gz',
},
{
format: 'tar.bz2',
url: 'https://example.com/gitlab-org/release-webhook-example/-/archive/v1.1/release-webhook-example-v1.1.tar.bz2',
},
{
format: 'tar',
url: 'https://example.com/gitlab-org/release-webhook-example/-/archive/v1.1/release-webhook-example-v1.1.tar',
},
],
},
commit: {
id: 'ee0a3fb31ac16e11b9dbb596ad16d4af654d08f8',
message: 'Release v1.1',
title: 'Release v1.1',
timestamp: '2020-10-31T14:58:32+11:00',
url: 'https://example.com/gitlab-org/release-webhook-example/-/commit/ee0a3fb31ac16e11b9dbb596ad16d4af654d08f8',
author: {
name: 'Example User',
email: 'user@example.com',
},
},
};

View File

@@ -0,0 +1,26 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './tag_push_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Tag event',
description:
'Tag event (triggered when you create or delete tags in the repository)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#tag-events',
key: GITLAB_EVENT_TYPE.tag_push_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.tag_push_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,43 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#tag-events
export default {
object_kind: 'tag_push',
event_name: 'tag_push',
before: '0000000000000000000000000000000000000000',
after: '82b3d5ae55f7080f1e6022629cdb57bfae7cccc7',
ref: 'refs/tags/v1.0.0',
checkout_sha: '82b3d5ae55f7080f1e6022629cdb57bfae7cccc7',
user_id: 1,
user_name: 'John Smith',
user_avatar:
'https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80',
project_id: 1,
project: {
id: 1,
name: 'Example',
description: '',
web_url: 'http://example.com/jsmith/example',
avatar_url: null,
git_ssh_url: 'git@example.com:jsmith/example.git',
git_http_url: 'http://example.com/jsmith/example.git',
namespace: 'Jsmith',
visibility_level: 0,
path_with_namespace: 'jsmith/example',
default_branch: 'master',
homepage: 'http://example.com/jsmith/example',
url: 'git@example.com:jsmith/example.git',
ssh_url: 'git@example.com:jsmith/example.git',
http_url: 'http://example.com/jsmith/example.git',
},
repository: {
name: 'Example',
url: 'ssh://git@example.com/jsmith/example.git',
description: '',
homepage: 'http://example.com/jsmith/example',
git_http_url: 'http://example.com/jsmith/example.git',
git_ssh_url: 'git@example.com:jsmith/example.git',
visibility_level: 0,
},
commits: [],
total_commits_count: 0,
};

View File

@@ -0,0 +1,24 @@
export enum GITLAB_EVENT_TYPE {
// ref: https://docs.gitlab.com/ee/api/projects.html#add-project-hook
confidential_issues_events = 'confidential_issues_events',
confidential_note_events = 'confidential_note_events',
deployment_events = 'deployment_events',
feature_flag_events = 'feature_flag_events',
issues_events = 'issues_events',
job_events = 'job_events',
merge_requests_events = 'merge_requests_events',
note_events = 'note_events',
pipeline_events = 'pipeline_events',
push_events = 'push_events',
releases_events = 'releases_events',
tag_push_events = 'tag_push_events',
wiki_page_events = 'wiki_page_events',
}
export type EventDescriptor = {
name: string;
description: string;
info?: string;
type: GITLAB_EVENT_TYPE;
data: any;
};

View File

@@ -0,0 +1,26 @@
import { IRawTrigger } from '@automatisch/types';
import defineTrigger from '../../../../helpers/define-trigger';
import { GITLAB_EVENT_TYPE } from '../types';
import {
getRegisterHookFn,
getTestRunFn,
projectArgumentDescriptor,
unregisterHook,
} from '../lib';
import data from './wiki_page_event';
export const triggerDescriptor: IRawTrigger = {
name: 'Wiki page event',
description:
'Wiki page event (triggered when a wiki page is created, updated, or deleted)',
// info: 'https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#wiki-page-events',
key: GITLAB_EVENT_TYPE.wiki_page_events,
type: 'webhook',
arguments: [projectArgumentDescriptor],
testRun: getTestRunFn(data),
registerHook: getRegisterHookFn(GITLAB_EVENT_TYPE.wiki_page_events),
unregisterHook,
};
export default defineTrigger(triggerDescriptor);

View File

@@ -0,0 +1,48 @@
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#wiki-page-events
export default {
object_kind: 'wiki_page',
user: {
id: 1,
name: 'Administrator',
username: 'root',
avatar_url:
'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
email: 'admin@example.com',
},
project: {
id: 1,
name: 'awesome-project',
description: 'This is awesome',
web_url: 'http://example.com/root/awesome-project',
avatar_url: null,
git_ssh_url: 'git@example.com:root/awesome-project.git',
git_http_url: 'http://example.com/root/awesome-project.git',
namespace: 'root',
visibility_level: 0,
path_with_namespace: 'root/awesome-project',
default_branch: 'master',
homepage: 'http://example.com/root/awesome-project',
url: 'git@example.com:root/awesome-project.git',
ssh_url: 'git@example.com:root/awesome-project.git',
http_url: 'http://example.com/root/awesome-project.git',
},
wiki: {
web_url: 'http://example.com/root/awesome-project/-/wikis/home',
git_ssh_url: 'git@example.com:root/awesome-project.wiki.git',
git_http_url: 'http://example.com/root/awesome-project.wiki.git',
path_with_namespace: 'root/awesome-project.wiki',
default_branch: 'master',
},
object_attributes: {
title: 'Awesome',
content: 'awesome content goes here',
format: 'markdown',
message: 'adding an awesome page to the wiki',
slug: 'awesome',
url: 'http://example.com/root/awesome-project/-/wikis/awesome',
action: 'create',
diff_url:
'http://example.com/root/awesome-project/-/wikis/home/diff?version_id=78ee4a6705abfbff4f4132c6646dbaae9c8fb6ec',
},
};

View File

@@ -1,7 +1,7 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List Folders',
name: 'List folders',
key: 'listFolders',
async run($: IGlobalVariable) {
@@ -11,13 +11,20 @@ export default {
data: [],
};
const params = {
const params: Record<string, unknown> = {
q: `mimeType='application/vnd.google-apps.folder'`,
orderBy: 'createdTime desc',
pageToken: undefined as unknown as string,
pageSize: 1000,
driveId: $.step.parameters.driveId,
supportsAllDrives: true,
};
if ($.step.parameters.driveId) {
params.includeItemsFromAllDrives = true;
params.corpora = 'drive';
}
do {
const { data } = await $.http.get(
`https://www.googleapis.com/drive/v3/files`,

View File

@@ -2,7 +2,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import newFilesInFolder from './new-files-in-folder';
export default defineTrigger({
name: 'New Files in Folder',
name: 'New files in folder',
key: 'newFilesInFolder',
pollInterval: 15,
description:
@@ -32,6 +32,7 @@ export default defineTrigger({
key: 'folderId',
type: 'dropdown' as const,
required: false,
dependsOn: ['parameters.driveId'],
description:
'Check a specific folder for new files. Please note: new files added to subfolders inside the folder you choose here will NOT trigger this flow. Defaults to the top-level folder if none is picked.',
variables: false,
@@ -43,6 +44,10 @@ export default defineTrigger({
name: 'key',
value: 'listFolders',
},
{
name: 'parameters.driveId',
value: '{parameters.driveId}',
},
],
},
},

View File

@@ -7,15 +7,21 @@ const newFilesInFolder = async ($: IGlobalVariable) => {
} else {
q += ` and parents in 'root'`;
}
const params = {
const params: Record<string, unknown> = {
pageToken: undefined as unknown as string,
orderBy: 'createdTime desc',
fields: '*',
pageSize: 1000,
q,
driveId: $.step.parameters.driveId,
supportsAllDrives: true,
};
if ($.step.parameters.driveId) {
params.includeItemsFromAllDrives = true;
params.corpora = 'drive';
}
do {
const { data } = await $.http.get(`/v3/files`, { params });
params.pageToken = data.nextPageToken;

View File

@@ -2,7 +2,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import newFiles from './new-files';
export default defineTrigger({
name: 'New Files',
name: 'New files',
key: 'newFiles',
pollInterval: 15,
description: 'Triggers when any new file is added (inside of any folder).',

View File

@@ -1,15 +1,21 @@
import { IGlobalVariable } from '@automatisch/types';
const newFiles = async ($: IGlobalVariable) => {
const params = {
const params: Record<string, unknown> = {
pageToken: undefined as unknown as string,
orderBy: 'createdTime desc',
fields: '*',
pageSize: 1000,
q: `mimeType!='application/vnd.google-apps.folder'`,
driveId: $.step.parameters.driveId,
supportsAllDrives: true,
};
if ($.step.parameters.driveId) {
params.includeItemsFromAllDrives = true;
params.corpora = 'drive';
}
do {
const { data } = await $.http.get('/v3/files', { params });
params.pageToken = data.nextPageToken;

View File

@@ -2,7 +2,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import newFolders from './new-folders';
export default defineTrigger({
name: 'New Folders',
name: 'New folders',
key: 'newFolders',
pollInterval: 15,
description:
@@ -32,6 +32,7 @@ export default defineTrigger({
key: 'folderId',
type: 'dropdown' as const,
required: false,
dependsOn: ['parameters.driveId'],
description:
'Check a specific folder for new subfolders. Please note: new folders added to subfolders inside the folder you choose here will NOT trigger this flow. Defaults to the top-level folder if none is picked.',
variables: false,
@@ -43,6 +44,10 @@ export default defineTrigger({
name: 'key',
value: 'listFolders',
},
{
name: 'parameters.driveId',
value: '{parameters.driveId}',
},
],
},
},

View File

@@ -8,15 +8,21 @@ const newFolders = async ($: IGlobalVariable) => {
q += ` and parents in 'root'`;
}
const params = {
const params: Record<string, unknown> = {
pageToken: undefined as unknown as string,
orderBy: 'createdTime desc',
fields: '*',
pageSize: 1000,
q,
driveId: $.step.parameters.driveId,
supportsAllDrives: true,
};
if ($.step.parameters.driveId) {
params.includeItemsFromAllDrives = true;
params.corpora = 'drive';
}
do {
const { data } = await $.http.get(`/v3/files`, { params });
params.pageToken = data.nextPageToken;

View File

@@ -2,7 +2,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import updatedFiles from './updated-files';
export default defineTrigger({
name: 'Updated Files',
name: 'Updated files',
key: 'updatedFiles',
pollInterval: 15,
description:
@@ -32,6 +32,7 @@ export default defineTrigger({
key: 'folderId',
type: 'dropdown' as const,
required: false,
dependsOn: ['parameters.driveId'],
description:
'Check a specific folder for updated files. Please note: files located in subfolders of the folder you choose here will NOT trigger this flow. Defaults to the top-level folder if none is picked.',
source: {
@@ -42,6 +43,10 @@ export default defineTrigger({
name: 'key',
value: 'listFolders',
},
{
name: 'parameters.driveId',
value: '{parameters.driveId}',
},
],
},
},

View File

@@ -12,15 +12,21 @@ const updatedFiles = async ($: IGlobalVariable) => {
q += ` and parents in 'root'`;
}
const params = {
const params: Record<string, unknown> = {
pageToken: undefined as unknown as string,
orderBy: 'modifiedTime desc',
fields: '*',
pageSize: 1000,
q,
driveId: $.step.parameters.driveId,
supportsAllDrives: true,
};
if ($.step.parameters.driveId) {
params.includeItemsFromAllDrives = true;
params.corpora = 'drive';
}
do {
const { data } = await $.http.get(`/v3/files`, { params });
params.pageToken = data.nextPageToken;

View File

@@ -2,7 +2,7 @@ import defineTrigger from '../../../../helpers/define-trigger';
import newFormResponses from './new-form-responses';
export default defineTrigger({
name: 'New Form Responses',
name: 'New form responses',
key: 'newFormResponses',
pollInterval: 15,
description: 'Triggers when a new form response is submitted.',

View File

@@ -0,0 +1,145 @@
import defineAction from '../../../../helpers/define-action';
type TSheetsResponse = {
sheets: {
properties: {
sheetId: string;
title: string;
};
}[];
};
export default defineAction({
name: 'Create spreadsheet row',
key: 'createSpreadsheetRow',
description: 'Creates a new row in a specified spreadsheet.',
arguments: [
{
label: 'Drive',
key: 'driveId',
type: 'dropdown' as const,
required: false,
description:
'The Google Drive where your spreadsheet resides. If nothing is selected, then your personal Google Drive will be used.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDrives',
},
],
},
},
{
label: 'Spreadsheet',
key: 'spreadsheetId',
type: 'dropdown' as const,
required: true,
dependsOn: ['parameters.driveId'],
description: 'The spreadsheets in your Google Drive.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listSpreadsheets',
},
{
name: 'parameters.driveId',
value: '{parameters.driveId}',
},
],
},
},
{
label: 'Worksheet',
key: 'worksheetId',
type: 'dropdown' as const,
required: true,
dependsOn: ['parameters.spreadsheetId'],
description: 'The worksheets in your selected spreadsheet.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listWorksheets',
},
{
name: 'parameters.spreadsheetId',
value: '{parameters.spreadsheetId}',
},
],
},
additionalFields: {
type: 'query',
name: 'getDynamicFields',
arguments: [
{
name: 'key',
value: 'listSheetHeaders',
},
{
name: 'parameters.worksheetId',
value: '{parameters.worksheetId}',
},
{
name: 'parameters.spreadsheetId',
value: '{parameters.spreadsheetId}',
},
],
},
},
],
async run($) {
const {
data: { sheets },
} = await $.http.get<TSheetsResponse>(
`/v4/spreadsheets/${$.step.parameters.spreadsheetId}`
);
const selectedSheet = sheets.find(
(sheet) => sheet.properties.sheetId === $.step.parameters.worksheetId
);
const sheetName = selectedSheet.properties.title;
const range = sheetName;
const dataValues = Object.entries($.step.parameters)
.filter((entry: [string, string]) => entry[0].startsWith('header-'))
.map((value) => value[1]);
const values = [dataValues];
const params = {
valueInputOption: 'USER_ENTERED',
insertDataOption: 'INSERT_ROWS',
includeValuesInResponse: true,
};
const body = {
majorDimension: 'ROWS',
range,
values,
};
const { data } = await $.http.post(
`/v4/spreadsheets/${$.step.parameters.spreadsheetId}/values/${range}:append`,
body,
{ params }
);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,105 @@
import defineAction from '../../../../helpers/define-action';
type THeaders = {
__id: string;
header: string;
}[];
export default defineAction({
name: 'Create spreadsheet',
key: 'createSpreadsheet',
description:
'Create a blank spreadsheet or duplicate an existing spreadsheet. Optionally, provide headers.',
arguments: [
{
label: 'Title',
key: 'title',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
{
label: 'Spreadsheet to copy',
key: 'spreadsheetId',
type: 'dropdown' as const,
required: false,
description: 'Choose a spreadsheet to copy its data.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listSpreadsheets',
},
],
},
},
{
label: 'Headers',
key: 'headers',
type: 'dynamic' as const,
required: false,
description:
'These headers are ignored if "Spreadsheet to Copy" is selected.',
fields: [
{
label: 'Header',
key: 'header',
type: 'string' as const,
required: true,
variables: true,
},
],
},
],
async run($) {
if ($.step.parameters.spreadsheetId) {
const body = { name: $.step.parameters.title };
const { data } = await $.http.post(
`https://www.googleapis.com/drive/v3/files/${$.step.parameters.spreadsheetId}/copy`,
body
);
$.setActionItem({
raw: data,
});
} else {
const headers = $.step.parameters.headers as THeaders;
const values = headers.map((entry) => entry.header);
const spreadsheetBody = {
properties: {
title: $.step.parameters.title,
},
sheets: [
{
data: [
{
startRow: 0,
startColumn: 0,
rowData: [
{
values: values.map((header) => ({
userEnteredValue: { stringValue: header },
})),
},
],
},
],
},
],
};
const { data } = await $.http.post('/v4/spreadsheets', spreadsheetBody);
$.setActionItem({
raw: data,
});
}
},
});

View File

@@ -0,0 +1,191 @@
import { IJSONObject } from '@automatisch/types';
import defineAction from '../../../../helpers/define-action';
type THeaders = {
__id: string;
header: string;
}[];
type TSheetsResponse = {
sheets: {
properties: {
sheetId: string;
title: string;
};
}[];
};
type TBody = {
requests: IJSONObject[];
};
export default defineAction({
name: 'Create worksheet',
key: 'createWorksheet',
description:
'Create a blank worksheet with a title. Optionally, provide headers.',
arguments: [
{
label: 'Drive',
key: 'driveId',
type: 'dropdown' as const,
required: false,
description:
'The Google Drive where your spreadsheet resides. If nothing is selected, then your personal Google Drive will be used.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDrives',
},
],
},
},
{
label: 'Spreadsheet',
key: 'spreadsheetId',
type: 'dropdown' as const,
required: true,
dependsOn: ['parameters.driveId'],
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listSpreadsheets',
},
{
name: 'parameters.driveId',
value: '{parameters.driveId}',
},
],
},
},
{
label: 'Title',
key: 'title',
type: 'string' as const,
required: true,
description: '',
variables: true,
},
{
label: 'Headers',
key: 'headers',
type: 'dynamic' as const,
required: false,
fields: [
{
label: 'Header',
key: 'header',
type: 'string' as const,
required: true,
variables: true,
},
],
},
{
label: 'Overwrite',
key: 'overwrite',
type: 'dropdown' as const,
required: false,
value: false,
description:
'If a worksheet with the specified title exists, its content would be lost. Please, use with caution.',
variables: true,
options: [
{
label: 'Yes',
value: 'true',
},
{
label: 'No',
value: 'false',
},
],
},
],
async run($) {
const {
data: { sheets },
} = await $.http.get<TSheetsResponse>(
`/v4/spreadsheets/${$.step.parameters.spreadsheetId}`
);
const selectedSheet = sheets.find(
(sheet) => sheet.properties.title === $.step.parameters.title
);
const headers = $.step.parameters.headers as THeaders;
const values = headers.map((entry) => entry.header);
const body: TBody = {
requests: [
{
addSheet: {
properties: {
title: $.step.parameters.title,
},
},
},
],
};
if ($.step.parameters.overwrite === 'true' && selectedSheet) {
body.requests.unshift({
deleteSheet: {
sheetId: selectedSheet.properties.sheetId,
},
});
}
const { data } = await $.http.post(
`https://sheets.googleapis.com/v4/spreadsheets/${$.step.parameters.spreadsheetId}:batchUpdate`,
body
);
if (values.length) {
const body = {
requests: [
{
updateCells: {
rows: [
{
values: values.map((header) => ({
userEnteredValue: { stringValue: header },
})),
},
],
fields: '*',
start: {
sheetId:
data.replies[data.replies.length - 1].addSheet.properties
.sheetId,
rowIndex: 0,
columnIndex: 0,
},
},
},
],
};
const { data: response } = await $.http.post(
`https://sheets.googleapis.com/v4/spreadsheets/${$.step.parameters.spreadsheetId}:batchUpdate`,
body
);
$.setActionItem({
raw: response,
});
return;
}
$.setActionItem({
raw: data,
});
},
});

View File

@@ -0,0 +1,5 @@
import createSpreadsheet from './create-spreadsheet';
import createSpreadsheetRow from './create-spreadsheet-row';
import createWorksheet from './create-worksheet';
export default [createSpreadsheet, createSpreadsheetRow, createWorksheet];

View File

@@ -1,3 +1,5 @@
import listDrives from './list-drives';
import listSpreadsheets from './list-spreadsheets';
import listWorksheets from './list-worksheets';
export default [listDrives];
export default [listDrives, listSpreadsheets, listWorksheets];

View File

@@ -0,0 +1,47 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List spreadsheets',
key: 'listSpreadsheets',
async run($: IGlobalVariable) {
const spreadsheets: {
data: IJSONObject[];
} = {
data: [],
};
const params: Record<string, unknown> = {
q: `mimeType='application/vnd.google-apps.spreadsheet'`,
pageSize: 100,
pageToken: undefined as unknown as string,
orderBy: 'createdTime desc',
driveId: $.step.parameters.driveId,
supportsAllDrives: true,
};
if ($.step.parameters.driveId) {
params.includeItemsFromAllDrives = true;
params.corpora = 'drive';
}
do {
const { data } = await $.http.get(
`https://www.googleapis.com/drive/v3/files`,
{ params }
);
params.pageToken = data.nextPageToken;
if (data.files?.length) {
for (const file of data.files) {
spreadsheets.data.push({
value: file.id,
name: file.name,
});
}
}
} while (params.pageToken);
return spreadsheets;
},
};

View File

@@ -0,0 +1,42 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List worksheets',
key: 'listWorksheets',
async run($: IGlobalVariable) {
const spreadsheetId = $.step.parameters.spreadsheetId as string;
const worksheets: {
data: IJSONObject[];
} = {
data: [],
};
if (!spreadsheetId) {
return worksheets;
}
const params: Record<string, unknown> = {
pageToken: undefined as unknown as string,
};
do {
const { data } = await $.http.get(`/v4/spreadsheets/${spreadsheetId}`, {
params,
});
params.pageToken = data.nextPageToken;
if (data.sheets?.length) {
for (const sheet of data.sheets) {
worksheets.data.push({
value: sheet.properties.sheetId,
name: sheet.properties.title,
});
}
}
} while (params.pageToken);
return worksheets;
},
};

View File

@@ -0,0 +1,3 @@
import listSheetHeaders from './list-sheet-headers';
export default [listSheetHeaders];

View File

@@ -0,0 +1,71 @@
import { IGlobalVariable } from '@automatisch/types';
type TSheetsResponse = {
sheets: {
properties: {
sheetId: string;
title: string;
};
}[];
};
type TSheetsValueResponse = {
majorDimension: string;
range: string;
values: string[][];
};
const hasValue = (value: any) => value !== null && value !== undefined;
export default {
name: 'List Sheet Headers',
key: 'listSheetHeaders',
async run($: IGlobalVariable) {
if (!hasValue($.step.parameters.spreadsheetId) || !hasValue($.step.parameters.worksheetId)) {
return;
}
const {
data: { sheets },
} = await $.http.get<TSheetsResponse>(
`/v4/spreadsheets/${$.step.parameters.spreadsheetId}`
);
const selectedSheet = sheets.find(
(sheet) => sheet.properties.sheetId === $.step.parameters.worksheetId
);
if (!selectedSheet) return;
const sheetName = selectedSheet.properties.title;
const range = `${sheetName}!1:1`;
const params: Record<string, unknown> = {
majorDimension: 'ROWS',
};
const { data } = await $.http.get<TSheetsValueResponse>(
`/v4/spreadsheets/${$.step.parameters.spreadsheetId}/values/${range}`,
{
params,
}
);
if (!data.values) {
return;
}
const result = data.values[0].map((item: string, index: number) => ({
label: item,
key: `header-${index}`,
type: 'string' as const,
required: false,
value: item,
variables: true,
}));
return result;
},
};

View File

@@ -2,7 +2,9 @@ import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import triggers from './triggers';
import actions from './actions';
import dynamicData from './dynamic-data';
import dynamicFields from './dynamic-fields';
export default defineApp({
name: 'Google Sheets',
@@ -16,5 +18,7 @@ export default defineApp({
beforeRequest: [addAuthHeader],
auth,
triggers,
actions,
dynamicData,
dynamicFields,
});

View File

@@ -1,3 +1,5 @@
import newSpreadsheets from './new-spreadsheets';
import newWorksheets from './new-worksheets';
import newSpreadsheetRows from './new-spreadsheet-rows';
export default [newSpreadsheets];
export default [newSpreadsheets, newWorksheets, newSpreadsheetRows];

View File

@@ -0,0 +1,82 @@
import defineTrigger from '../../../../helpers/define-trigger';
import newSpreadsheetRows from './new-spreadsheet-rows';
export default defineTrigger({
name: 'New spreadsheet rows',
key: 'newSpreadsheetRows',
pollInterval: 15,
description:
'Triggers when a new row is added to the bottom of a spreadsheet.',
arguments: [
{
label: 'Drive',
key: 'driveId',
type: 'dropdown' as const,
required: false,
description:
'The Google Drive where your spreadsheet resides. If nothing is selected, then your personal Google Drive will be used.',
variables: false,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDrives',
},
],
},
},
{
label: 'Spreadsheet',
key: 'spreadsheetId',
type: 'dropdown' as const,
required: true,
dependsOn: ['parameters.driveId'],
description: 'The spreadsheets in your Google Drive.',
variables: false,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listSpreadsheets',
},
{
name: 'parameters.driveId',
value: '{parameters.driveId}',
},
],
},
},
{
label: 'Worksheet',
key: 'worksheetId',
type: 'dropdown' as const,
required: true,
dependsOn: ['parameters.spreadsheetId'],
description:
'The worksheets in your selected spreadsheet. You must have column headers.',
variables: false,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listWorksheets',
},
{
name: 'parameters.spreadsheetId',
value: '{parameters.spreadsheetId}',
},
],
},
},
],
async run($) {
await newSpreadsheetRows($);
},
});

View File

@@ -0,0 +1,46 @@
import { IGlobalVariable } from '@automatisch/types';
type TSheetsResponse = {
sheets: {
properties: {
sheetId: string;
title: string;
};
}[];
};
const newSpreadsheetRows = async ($: IGlobalVariable) => {
const {
data: { sheets },
} = await $.http.get<TSheetsResponse>(
`/v4/spreadsheets/${$.step.parameters.spreadsheetId}`
);
const selectedSheet = sheets.find(
(sheet) => sheet.properties.sheetId === $.step.parameters.worksheetId
);
if (!selectedSheet) return;
const sheetName = selectedSheet.properties.title;
const range = sheetName;
const { data } = await $.http.get(
`v4/spreadsheets/${$.step.parameters.spreadsheetId}/values/${range}`
);
if (data.values?.length) {
for (let index = data.values.length - 1; index > 0; index--) {
const value = data.values[index];
$.pushTriggerItem({
raw: { row: value },
meta: {
internalId: index.toString(),
},
});
}
}
};
export default newSpreadsheetRows;

View File

@@ -1,8 +1,8 @@
import defineTrigger from '../../../../helpers/define-trigger';
import newSpreadsheets from './new-spreadsheets'
import newSpreadsheets from './new-spreadsheets';
export default defineTrigger({
name: 'New Spreadsheets',
name: 'New spreadsheets',
key: 'newSpreadsheets',
pollInterval: 15,
description: 'Triggers when you create a new spreadsheet.',
@@ -12,7 +12,8 @@ export default defineTrigger({
key: 'driveId',
type: 'dropdown' as const,
required: false,
description: 'The Google Drive where your spreadsheet resides. If nothing is selected, then your personal Google Drive will be used.',
description:
'The Google Drive where your spreadsheet resides. If nothing is selected, then your personal Google Drive will be used.',
variables: false,
source: {
type: 'query',
@@ -30,4 +31,4 @@ export default defineTrigger({
async run($) {
await newSpreadsheets($);
},
});
});

View File

@@ -1,15 +1,21 @@
import { IGlobalVariable } from '@automatisch/types';
const newSpreadsheets = async ($: IGlobalVariable) => {
const params = {
const params: Record<string, unknown> = {
pageToken: undefined as unknown as string,
orderBy: 'createdTime desc',
q: `mimeType='application/vnd.google-apps.spreadsheet'`,
fields: '*',
pageSize: 1000,
driveId: $.step.parameters.driveId,
supportsAllDrives: true,
};
if ($.step.parameters.driveId) {
params.includeItemsFromAllDrives = true;
params.corpora = 'drive';
}
do {
const { data } = await $.http.get(
'https://www.googleapis.com/drive/v3/files',

View File

@@ -0,0 +1,57 @@
import defineTrigger from '../../../../helpers/define-trigger';
import newWorksheets from './new-worksheets';
export default defineTrigger({
name: 'New worksheets',
key: 'newWorksheets',
pollInterval: 15,
description: 'Triggers when you create a new worksheet in a spreadsheet.',
arguments: [
{
label: 'Drive',
key: 'driveId',
type: 'dropdown' as const,
required: false,
description:
'The Google Drive where your spreadsheet resides. If nothing is selected, then your personal Google Drive will be used.',
variables: false,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDrives',
},
],
},
},
{
label: 'Spreadsheet',
key: 'spreadsheetId',
type: 'dropdown' as const,
required: true,
dependsOn: ['parameters.driveId'],
description: 'The spreadsheets in your Google Drive.',
variables: false,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listSpreadsheets',
},
{
name: 'parameters.driveId',
value: '{parameters.driveId}',
},
],
},
},
],
async run($) {
await newWorksheets($);
},
});

View File

@@ -0,0 +1,28 @@
import { IGlobalVariable } from '@automatisch/types';
const newWorksheets = async ($: IGlobalVariable) => {
const params = {
pageToken: undefined as unknown as string,
};
do {
const { data } = await $.http.get(
`/v4/spreadsheets/${$.step.parameters.spreadsheetId}`,
{ params }
);
params.pageToken = data.nextPageToken;
if (data.sheets?.length) {
for (const sheet of data.sheets.reverse()) {
$.pushTriggerItem({
raw: sheet,
meta: {
internalId: sheet.properties.sheetId.toString(),
},
});
}
}
} while (params.pageToken);
};
export default newWorksheets;

View File

@@ -1,3 +1,4 @@
import type { AxiosRequestConfig } from 'axios';
import defineAction from '../../../../helpers/define-action';
type TMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
@@ -5,12 +6,31 @@ type TMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
type THeaderEntry = {
key: string;
value: string;
}
};
type THeaderEntries = THeaderEntry[];
function isPossiblyTextBased(contentType: string) {
if (!contentType) return false;
return (
contentType.startsWith('application/json') ||
contentType.startsWith('text/')
);
}
function throwIfFileSizeExceedsLimit(contentLength: string) {
const maxFileSize = 25 * 1024 * 1024; // 25MB
if (Number(contentLength) > maxFileSize) {
throw new Error(
`Response is too large. Maximum size is 25MB. Actual size is ${contentLength}`
);
}
}
export default defineAction({
name: 'Custom Request',
name: 'Custom request',
key: 'customRequest',
description: 'Makes a custom HTTP request by providing raw details.',
arguments: [
@@ -51,10 +71,12 @@ export default defineAction({
type: 'dynamic' as const,
required: false,
description: 'Add or remove headers as needed',
value: [{
key: 'Content-Type',
value: 'application/json'
}],
value: [
{
key: 'Content-Type',
value: 'application/json',
},
],
fields: [
{
label: 'Key',
@@ -62,7 +84,7 @@ export default defineAction({
type: 'string' as const,
required: true,
description: 'Header key',
variables: false,
variables: true,
},
{
label: 'Value',
@@ -71,9 +93,9 @@ export default defineAction({
required: true,
description: 'Header value',
variables: true,
}
},
],
}
},
],
async run($) {
@@ -81,29 +103,56 @@ export default defineAction({
const data = $.step.parameters.data as string;
const url = $.step.parameters.url as string;
const headers = $.step.parameters.headers as THeaderEntries;
const maxFileSize = 25 * 1024 * 1024; // 25MB
const headersObject = headers.reduce((result, entry) => ({ ...result, [entry.key]: entry.value }), {})
const headersObject: Record<string, string> = headers.reduce(
(result, entry) => {
const key = entry.key?.toLowerCase();
const value = entry.value;
const metadataResponse = await $.http.head(url, { headers: headersObject });
if (key && value) {
return {
...result,
[entry.key?.toLowerCase()]: entry.value,
};
}
if (Number(metadataResponse.headers['content-length']) > maxFileSize) {
throw new Error(
`Response is too large. Maximum size is 25MB. Actual size is ${metadataResponse.headers['content-length']}`
);
}
return result;
},
{}
);
const response = await $.http.request({
let contentType = headersObject['content-type'];
// in case HEAD request is not supported by the URL
try {
const metadataResponse = await $.http.head(url, {
headers: headersObject,
});
contentType = metadataResponse.headers['content-type'];
throwIfFileSizeExceedsLimit(metadataResponse.headers['content-length']);
// eslint-disable-next-line no-empty
} catch { }
const requestData: AxiosRequestConfig = {
url,
method,
data,
headers: headersObject,
});
};
if (!isPossiblyTextBased(contentType)) {
requestData.responseType = 'arraybuffer';
}
const response = await $.http.request(requestData);
throwIfFileSizeExceedsLimit(response.headers['content-length']);
let responseData = response.data;
if (typeof response.data === 'string') {
responseData = response.data.replaceAll('\u0000', '');
if (!isPossiblyTextBased(contentType)) {
responseData = Buffer.from(responseData as string).toString('base64');
}
$.setActionItem({ raw: { data: responseData } });

View File

@@ -0,0 +1,3 @@
import sendMessageToChannel from './send-a-message-to-channel';
export default [sendMessageToChannel];

View File

@@ -0,0 +1,42 @@
import defineAction from '../../../../helpers/define-action';
import postMessage from './post-message';
export default defineAction({
name: 'Send a message to channel',
key: 'sendMessageToChannel',
description: 'Sends a message to a channel you specify.',
arguments: [
{
label: 'Channel',
key: 'channel',
type: 'dropdown' as const,
required: true,
description: 'Pick a channel to send the message to.',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listChannels',
},
],
},
},
{
label: 'Message text',
key: 'message',
type: 'string' as const,
required: true,
description: 'The content of your new message.',
variables: true,
},
],
async run($) {
const message = await postMessage($);
return message;
},
});

View File

@@ -0,0 +1,27 @@
import { IGlobalVariable } from '@automatisch/types';
type TData = {
channel_id: string;
message: string;
};
const postMessage = async ($: IGlobalVariable) => {
const { parameters } = $.step;
const channel_id = parameters.channel as string;
const message = parameters.message as string;
const data: TData = {
channel_id,
message,
};
const response = await $.http.post('/api/v4/posts', data);
const actionData = {
raw: response?.data,
};
$.setActionItem(actionData);
};
export default postMessage;

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M6.79123171,86.9648684 C25.2351716,32.4823178 76.783459,-1.43234143 131.421839,0.0464773399 L131.421839,0.0464773399 L113.909757,20.739032 C81.4957329,26.5997669 53.5072568,48.7337413 42.5072761,81.2287969 C26.140539,129.576353 53.572705,182.545803 103.779303,199.543648 C153.985902,216.538377 207.952658,191.12264 224.319395,142.7782 C235.283535,110.390667 226.589826,75.9306053 204.563374,51.5978814 L204.563374,51.5978814 L203.21701,24.4290666 C247.371203,56.4768925 267.622761,114.633895 249.208429,169.029181 C226.546194,235.970273 153.909545,271.865521 86.9684532,249.204844 C20.0273609,226.542609 -15.8694453,153.905961 6.79123171,86.9648684 Z M165.185344,11.9237762 C165.839826,11.6401671 166.594039,11.5793938 167.321762,11.8256038 C168.035459,12.0671391 168.585536,12.5580009 168.936152,13.1595015 L168.936152,13.1595015 L169.007833,13.2763734 L169.071723,13.4103864 C169.240019,13.7313945 169.383381,14.0991514 169.450388,14.5510559 C169.582343,15.4417519 169.641535,17.5358595 169.665634,19.6808502 L169.671365,20.2662434 C169.677102,20.9486534 169.679633,21.6256073 169.680171,22.2599793 L169.680173,22.7924325 C169.678741,24.5267431 169.663874,25.8268542 169.663874,25.8268542 L169.663874,25.8268542 L170.167202,44.7600977 L170.910507,66.6151379 L171.837691,104.59538 C171.837691,104.59538 171.83785,104.602367 171.838064,104.616156 L171.838772,104.677745 C171.838883,104.691349 171.838983,104.706608 171.839058,104.723498 L171.839105,104.844231 C171.832023,107.013302 171.387173,122.892918 160.122454,133.928662 C148.009853,145.795053 133.131285,144.708923 123.451177,141.433394 C113.771069,138.154749 101.293828,129.979951 98.8800345,113.195592 C96.8283098,98.9302108 104.41287,86.9390787 106.734401,83.6627102 L106.889339,83.4459953 C107.205256,83.0081712 107.389865,82.7777388 107.389865,82.7777388 L107.389865,82.7777388 L131.197445,53.1717559 L145.064682,36.2627333 L156.965355,21.5275276 C156.965355,21.5275276 158.715313,19.1834331 160.51647,16.874806 L160.876881,16.4142586 C161.477025,15.6498178 162.070275,14.9069442 162.593713,14.2737698 L162.898895,13.907734 C163.342593,13.3805415 163.71955,12.9564826 163.983901,12.6998055 C164.292443,12.4006135 164.608776,12.205827 164.918876,12.0546727 L164.918876,12.0546727 L165.146386,11.9393591 Z" fill="#0058CC"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,18 @@
import { IGlobalVariable } from '@automatisch/types';
import { URL, URLSearchParams } from 'url';
import getBaseUrl from '../common/get-base-url';
export default async function generateAuthUrl($: IGlobalVariable) {
const searchParams = new URLSearchParams({
client_id: $.auth.data.clientId as string,
redirect_uri: $.auth.data.oAuthRedirectUrl as string,
response_type: 'code',
});
const baseUrl = getBaseUrl($);
const path = `/oauth/authorize?${searchParams.toString()}`;
await $.auth.set({
url: new URL(path, baseUrl).toString(),
});
}

View File

@@ -0,0 +1,57 @@
import generateAuthUrl from './generate-auth-url';
import verifyCredentials from './verify-credentials';
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/mattermost/connections/add',
placeholder: null,
description:
'When asked to input an OAuth callback or redirect URL in Mattermost OAuth, enter the URL above.',
clickToCopy: true,
},
{
key: 'instanceUrl',
label: 'Mattermost instance URL',
type: 'string' as const,
required: false,
readOnly: false,
value: null,
placeholder: null,
description: 'Your Mattermost instance URL',
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,
};

View File

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

View File

@@ -0,0 +1,44 @@
import { IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const verifyCredentials = async ($: IGlobalVariable) => {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const params = {
client_id: $.auth.data.clientId,
client_secret: $.auth.data.clientSecret,
code: $.auth.data.code,
grant_type: 'authorization_code',
redirect_uri: redirectUri,
};
const headers = {
'Content-Type': 'application/x-www-form-urlencoded', // This is not documented yet required
};
const response = await $.http.post('/oauth/access_token', null, {
params,
headers,
});
const {
data: { access_token, refresh_token, scope, token_type },
} = response;
$.auth.data.accessToken = response.data.access_token;
const currentUser = await getCurrentUser($);
await $.auth.set({
clientId: $.auth.data.clientId,
clientSecret: $.auth.data.clientSecret,
accessToken: access_token,
refreshToken: refresh_token,
scope: scope,
tokenType: token_type,
userId: currentUser.id,
screenName: currentUser.username,
});
};
export default verifyCredentials;

View File

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

View File

@@ -0,0 +1,11 @@
import { TBeforeRequest } from '@automatisch/types';
const addXRequestedWithHeader: TBeforeRequest = ($, requestConfig) => {
// This is not documented yet required
// ref. https://forum.mattermost.com/t/solved-invalid-or-expired-session-please-login-again/6772
requestConfig.headers = requestConfig.headers || {};
requestConfig.headers['X-Requested-With'] = `XMLHttpRequest`;
return requestConfig;
};
export default addXRequestedWithHeader;

View File

@@ -0,0 +1,7 @@
import { IGlobalVariable } from '@automatisch/types';
const getBaseUrl = ($: IGlobalVariable): string => {
return $.auth.data.instanceUrl as string;
};
export default getBaseUrl;

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