Compare commits

..

1 Commits

Author SHA1 Message Date
Faruk AYDIN
ff72b2ae35 Release v0.4.0 2023-01-13 17:29:30 +03:00
806 changed files with 13466 additions and 26648 deletions

View File

@@ -1 +0,0 @@
FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu-22.04

View File

@@ -1,45 +0,0 @@
#!/bin/bash
CURRENT_DIR="$(pwd)"
BACKEND_PORT=3000
WEB_PORT=3001
echo "Configuring backend environment variables..."
cd packages/backend
rm -rf .env
echo "
PORT=$BACKEND_PORT
WEB_APP_URL=http://localhost:$WEB_PORT
APP_ENV=development
POSTGRES_DATABASE=automatisch
POSTGRES_PORT=5432
POSTGRES_HOST=postgres
POSTGRES_USERNAME=automatisch_user
POSTGRES_PASSWORD=automatisch_password
ENCRYPTION_KEY=sample_encryption_key
WEBHOOK_SECRET_KEY=sample_webhook_secret_key
APP_SECRET_KEY=sample_app_secret_key
REDIS_HOST=redis
SERVE_WEB_APP_SEPARATELY=true" >> .env
cd $CURRENT_DIR
echo "Configuring web environment variables..."
cd packages/web
rm -rf .env
echo "
PORT=$WEB_PORT
REACT_APP_GRAPHQL_URL=http://localhost:$BACKEND_PORT/graphql
REACT_APP_NOTIFICATIONS_URL=https://notifications.automatisch.io
" >> .env
cd $CURRENT_DIR
echo "Installing and linking dependencies..."
yarn
yarn lerna bootstrap
echo "Migrating database..."
cd packages/backend
yarn db:migrate
yarn db:seed:user
echo "Done!"

View File

@@ -1,53 +0,0 @@
{
"name": "Automatisch",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers/features/git:1": {
"version": "latest"
},
"ghcr.io/devcontainers/features/node:1": {
"version": 16
},
"ghcr.io/devcontainers/features/common-utils:1": {
"username": "vscode",
"uid": 1000,
"gid": 1000,
"installZsh": true,
"installOhMyZsh": true,
"configureZshAsDefaultShell": true,
"upgradePackages": true
}
},
"hostRequirements": {
"cpus": 4,
"memory": "8gb",
"storage": "32gb"
},
"portsAttributes": {
"3000": {
"label": "Backend",
"onAutoForward": "silent",
"protocol": "http"
},
"3001": {
"label": "Frontend",
"onAutoForward": "silent",
"protocol": "http"
}
},
"forwardPorts": [3000, 3001],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": ["bash", ".devcontainer/boot.sh"]
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@@ -1,64 +0,0 @@
version: '3.9'
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
volumes:
- ..:/workspace:cached
command: sleep infinity
postgres:
image: 'postgres:14.5-alpine'
environment:
- POSTGRES_DB=automatisch
- POSTGRES_USER=automatisch_user
- POSTGRES_PASSWORD=automatisch_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
interval: 10s
timeout: 5s
retries: 5
ports:
- '5432:5432'
expose:
- 5432
redis:
image: 'redis:7.0.4-alpine'
volumes:
- redis_data:/data
ports:
- '6379:6379'
expose:
- 6379
keycloak:
image: quay.io/keycloak/keycloak:21.1
restart: always
container_name: keycloak
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
- KC_DB=postgres
- KC_DB_URL_HOST=postgres
- KC_DB_URL_DATABASE=keycloak
- KC_DB_USERNAME=automatisch_user
- KC_DB_PASSWORD=automatisch_password
- KC_HEALTH_ENABLED=true
ports:
- "8080:8080"
command: start-dev
depends_on:
- postgres
healthcheck:
test: "curl -f http://localhost:8080/health/ready || exit 1"
volumes:
- keycloak:/opt/keycloak/data/
expose:
- 8080
volumes:
postgres_data:
redis_data:
keycloak:

View File

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

View File

@@ -1,31 +0,0 @@
name: Automatisch UI Tests
on:
schedule:
- cron: '0 12 * * *'
workflow_dispatch:
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: yarn
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Run Playwright tests
working-directory: ./packages/e2e-tests
env:
LOGIN_EMAIL: ${{ secrets.LOGIN_EMAIL }}
LOGIN_PASSWORD: ${{ secrets.LOGIN_PASSWORD }}
run: yarn test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: test-results/
retention-days: 30

3
.gitignore vendored
View File

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

View File

@@ -1,5 +0,0 @@
# Automatisch Contributor License Agreement
I give Automatisch permission to license my contributions on any terms they like. I am giving them this license in order to make it possible for them to accept my contributions into their project.
**_As far as the law allows, my contributions come as is, without any warranty or condition, and I will not be liable to anyone for any damages related to this software or this license, under any kind of legal claim._**

View File

@@ -1,3 +0,0 @@
LICENSE.agpl (AGPL-3.0) applies to all files in this
repository, except for files that contain ".ee." in their name
which are covered by LICENSE.enterprise.

View File

@@ -1,35 +0,0 @@
The Automatisch Enterprise license (the “Enterprise License”)
Copyright (c) 2023-present AB Software GmbH.
With regard to the Automatisch Software:
This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have a valid
Automatisch Enterprise license for the correct number of user seats. Subject
to the foregoing sentence, you are free to modify this Software and publish
patches to the Software. You agree that Automatisch and/or its licensors
(as applicable) retain all right, title and interest in and to all such
modifications and/or patches, and all such modifications and/or patches may
only be used, copied, modified, displayed, distributed, or otherwise exploited
with a valid Automatisch Enterprise license for the correct number of user seats.
Notwithstanding the foregoing, you may copy and modify the Software for
development and testing purposes, without requiring a subscription. You agree
that Automatisch and/or its licensors (as applicable) retain all right, title
and interest in and to all such modifications. You are not granted any other
rights beyond what is expressly stated herein. Subject to the foregoing, it is
forbidden to copy, merge, publish, distribute, sublicense, and/or sell the Software.
The full text of this Enterprise License shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For all third party components incorporated into the Automatisch Software, those
components are licensed under the original license provided by the owner of the
applicable component.

View File

@@ -24,7 +24,7 @@ The official documentation can be found here: [https://automatisch.io/docs](http
```bash ```bash
# Clone the repository # Clone the repository
git clone https://github.com/automatisch/automatisch.git git clone git@github.com:automatisch/automatisch.git
# Go to the repository folder # Go to the repository folder
cd automatisch cd automatisch
@@ -44,18 +44,10 @@ For other installation types, you can check the [installation](https://automatis
## Support ## Support
If you have any questions or problems, please visit our GitHub issues page, and we'll try to help you as soon as possible. If you have any questions or problems, please visit our GitHub discussions page, and we'll try to help you as soon as possible.
[https://github.com/automatisch/automatisch/issues](https://github.com/automatisch/automatisch/issues) [https://github.com/automatisch/automatisch/discussions](https://github.com/automatisch/automatisch/discussions)
## License ## License
Automatisch Community Edition (Automatisch CE) is an open-source software with the [AGPL-3.0 license](LICENSE.agpl). Automatisch is an open-source software with the [AGPL 3.0 license](https://github.com/automatisch/automatisch/blob/main/LICENSE.md).
Automatisch Enterprise Edition (Automatisch EE) is a commercial offering with the [Enterprise license](LICENSE.enterprise).
The Automatisch repository contains both AGPL-licensed and Enterprise-licensed files. We maintain a single repository to make development easier.
All files that contain ".ee." in their name fall under the [Enterprise license](LICENSE.enterprise). All other files fall under the [AGPL-3.0 license](LICENSE.agpl).
See the [LICENSE](LICENSE) file for more information.

View File

@@ -2,13 +2,13 @@
FROM node:16-alpine FROM node:16-alpine
WORKDIR /automatisch WORKDIR /automatisch
RUN \ RUN apk --no-cache add --virtual build-dependencies python3 build-base
apk --no-cache add --virtual build-dependencies python3 build-base && \
yarn global add @automatisch/cli@0.9.1 --network-timeout 1000000 && \
rm -rf /usr/local/share/.cache/ && \
apk del build-dependencies
COPY ./entrypoint.sh /entrypoint.sh COPY ./entrypoint.sh /entrypoint.sh
RUN yarn global add @automatisch/cli@0.3.0
RUN apk del build-dependencies python3 build-base
EXPOSE 3000 EXPOSE 3000
ENTRYPOINT ["sh", "/entrypoint.sh"] ENTRYPOINT ["sh", "/entrypoint.sh"]

View File

@@ -1,19 +0,0 @@
# 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 # syntax=docker/dockerfile:1
FROM automatischio/automatisch:0.9.1 FROM automatischio/automatisch:0.3.0
WORKDIR /automatisch WORKDIR /automatisch
RUN apk add --no-cache openssl dos2unix RUN apk add --no-cache openssl dos2unix

View File

@@ -1,9 +0,0 @@
#!/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": [
"packages/*" "packages/*"
], ],
"version": "0.9.1", "version": "0.4.0",
"npmClient": "yarn", "npmClient": "yarn",
"useWorkspaces": true, "useWorkspaces": true,
"command": { "command": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@automatisch/root", "name": "@automatisch/root",
"license": "See LICENSE file", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "lerna run --stream --parallel --scope=@*/{web,backend} dev", "start": "lerna run --stream --parallel --scope=@*/{web,backend} dev",

View File

@@ -2,33 +2,16 @@ import appConfig from '../../src/config/app';
import logger from '../../src/helpers/logger'; import logger from '../../src/helpers/logger';
import client from './client'; import client from './client';
import User from '../../src/models/user'; import User from '../../src/models/user';
import Role from '../../src/models/role';
import '../../src/config/orm'; import '../../src/config/orm';
async function fetchAdminRole() {
const role = await Role
.query()
.where({
key: 'admin'
})
.limit(1)
.first();
return role;
}
export async function createUser( export async function createUser(
email = 'user@automatisch.io', email = 'user@automatisch.io',
password = 'sample' password = 'sample'
) { ) {
const UNIQUE_VIOLATION_CODE = '23505'; const UNIQUE_VIOLATION_CODE = '23505';
const role = await fetchAdminRole();
const userParams = { const userParams = {
email, email,
password, password,
fullName: 'Initial admin',
roleId: role.id,
}; };
try { try {

View File

@@ -12,8 +12,6 @@ const knexConfig = {
database: appConfig.postgresDatabase, database: appConfig.postgresDatabase,
ssl: appConfig.postgresEnableSsl, ssl: appConfig.postgresEnableSsl,
}, },
asyncStackTraces: appConfig.isDev,
searchPath: [appConfig.postgresSchema],
pool: { min: 0, max: 20 }, pool: { min: 0, max: 20 },
migrations: { migrations: {
directory: __dirname + '/src/db/migrations', directory: __dirname + '/src/db/migrations',

View File

@@ -1,10 +1,10 @@
{ {
"name": "@automatisch/backend", "name": "@automatisch/backend",
"version": "0.9.1", "version": "0.4.0",
"license": "See LICENSE file", "license": "AGPL-3.0",
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.", "description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
"scripts": { "scripts": {
"dev": "ts-node-dev --watch 'src/graphql/schema.graphql' --exit-child src/server.ts", "dev": "ts-node-dev --exit-child src/server.ts",
"worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts", "worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts",
"build": "tsc && yarn copy-statics", "build": "tsc && yarn copy-statics",
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts", "build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
@@ -17,23 +17,17 @@
"db:migration:create": "knex migrate:make", "db:migration:create": "knex migrate:make",
"db:rollback": "knex migrate:rollback", "db:rollback": "knex migrate:rollback",
"db:migrate": "knex migrate:latest", "db:migrate": "knex migrate:latest",
"copy-statics": "copyfiles src/**/*.{graphql,json,svg,hbs} dist", "copy-statics": "copyfiles src/**/*.{graphql,json,svg} dist",
"prepack": "yarn build", "prepack": "yarn build",
"prebuild": "rm -rf ./dist" "prebuild": "rm -rf ./dist"
}, },
"dependencies": { "dependencies": {
"@automatisch/web": "^0.9.1", "@automatisch/web": "^0.4.0",
"@bull-board/express": "^3.10.1", "@bull-board/express": "^3.10.1",
"@casl/ability": "^6.5.0",
"@graphql-tools/graphql-file-loader": "^7.3.4", "@graphql-tools/graphql-file-loader": "^7.3.4",
"@graphql-tools/load": "^7.5.2", "@graphql-tools/load": "^7.5.2",
"@node-saml/passport-saml": "^4.0.4",
"@rudderstack/rudder-sdk-node": "^1.1.2", "@rudderstack/rudder-sdk-node": "^1.1.2",
"@sentry/node": "^7.42.0",
"@sentry/tracing": "^7.42.0",
"@types/luxon": "^2.3.1", "@types/luxon": "^2.3.1",
"@types/passport": "^1.0.12",
"@types/xmlrpc": "^1.3.7",
"ajv-formats": "^2.1.1", "ajv-formats": "^2.1.1",
"axios": "0.24.0", "axios": "0.24.0",
"bcrypt": "^5.0.1", "bcrypt": "^5.0.1",
@@ -51,28 +45,18 @@
"graphql-shield": "^7.5.0", "graphql-shield": "^7.5.0",
"graphql-tools": "^8.2.0", "graphql-tools": "^8.2.0",
"graphql-type-json": "^0.3.2", "graphql-type-json": "^0.3.2",
"handlebars": "^4.7.7",
"http-errors": "~1.6.3", "http-errors": "~1.6.3",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.1",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"knex": "^2.4.0", "knex": "^0.95.11",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"luxon": "2.5.2", "luxon": "2.5.2",
"memory-cache": "^0.2.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"node-html-markdown": "^1.3.0",
"nodemailer": "6.7.0", "nodemailer": "6.7.0",
"oauth-1.0a": "^2.2.6", "oauth-1.0a": "^2.2.6",
"objection": "^3.0.0", "objection": "^3.0.0",
"passport": "^0.6.0",
"pg": "^8.7.1", "pg": "^8.7.1",
"php-serialize": "^4.0.2", "winston": "^3.7.1"
"showdown": "^2.1.0",
"stripe": "^11.13.0",
"winston": "^3.7.1",
"xmlrpc": "^1.3.2"
}, },
"contributors": [ "contributors": [
{ {
@@ -110,7 +94,7 @@
"url": "https://github.com/automatisch/automatisch/issues" "url": "https://github.com/automatisch/automatisch/issues"
}, },
"devDependencies": { "devDependencies": {
"@automatisch/types": "^0.9.1", "@automatisch/types": "^0.4.0",
"@types/bcrypt": "^5.0.0", "@types/bcrypt": "^5.0.0",
"@types/bull": "^3.15.8", "@types/bull": "^3.15.8",
"@types/cors": "^2.8.12", "@types/cors": "^2.8.12",
@@ -119,14 +103,12 @@
"@types/http-errors": "^1.8.1", "@types/http-errors": "^1.8.1",
"@types/jsonwebtoken": "^8.5.8", "@types/jsonwebtoken": "^8.5.8",
"@types/lodash.get": "^4.4.6", "@types/lodash.get": "^4.4.6",
"@types/memory-cache": "^0.2.2",
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.3",
"@types/multer": "1.4.7", "@types/multer": "1.4.7",
"@types/node": "^16.10.2", "@types/node": "^16.10.2",
"@types/nodemailer": "^6.4.4", "@types/nodemailer": "^6.4.4",
"@types/pg": "^8.6.1", "@types/pg": "^8.6.1",
"@types/pino": "^7.0.5", "@types/pino": "^7.0.5",
"@types/showdown": "^2.0.1",
"ava": "^3.15.0", "ava": "^3.15.0",
"nodemon": "^2.0.13", "nodemon": "^2.0.13",
"sinon": "^11.1.2", "sinon": "^11.1.2",

View File

@@ -1,12 +1,8 @@
import createError from 'http-errors'; import createError from 'http-errors';
import express from 'express'; import express from 'express';
import cors from 'cors'; import cors from 'cors';
import { IRequest } from '@automatisch/types';
import appConfig from './config/app';
import corsOptions from './config/cors-options'; import corsOptions from './config/cors-options';
import morgan from './helpers/morgan'; import morgan from './helpers/morgan';
import * as Sentry from './helpers/sentry.ee';
import appAssetsHandler from './helpers/app-assets-handler'; import appAssetsHandler from './helpers/app-assets-handler';
import webUIHandler from './helpers/web-ui-handler'; import webUIHandler from './helpers/web-ui-handler';
import errorHandler from './helpers/error-handler'; import errorHandler from './helpers/error-handler';
@@ -17,17 +13,12 @@ import {
} from './helpers/create-bull-board-handler'; } from './helpers/create-bull-board-handler';
import injectBullBoardHandler from './helpers/inject-bull-board-handler'; import injectBullBoardHandler from './helpers/inject-bull-board-handler';
import router from './routes'; import router from './routes';
import configurePassport from './helpers/passport'; import { IRequest } from '@automatisch/types';
createBullBoardHandler(serverAdapter); createBullBoardHandler(serverAdapter);
const app = express(); const app = express();
Sentry.init(app);
Sentry.attachRequestHandler(app);
Sentry.attachTracingHandler(app);
injectBullBoardHandler(app, serverAdapter); injectBullBoardHandler(app, serverAdapter);
appAssetsHandler(app); appAssetsHandler(app);
@@ -35,25 +26,18 @@ appAssetsHandler(app);
app.use(morgan); app.use(morgan);
app.use( app.use(
express.json({ express.json({
limit: appConfig.requestBodySizeLimit,
verify(req, res, buf) { verify(req, res, buf) {
(req as IRequest).rawBody = buf; (req as IRequest).rawBody = buf;
}, },
}) })
); );
app.use( app.use(express.urlencoded({
express.urlencoded({ extended: false,
extended: true,
limit: appConfig.requestBodySizeLimit,
verify(req, res, buf) { verify(req, res, buf) {
(req as IRequest).rawBody = buf; (req as IRequest).rawBody = buf;
}, },
}) }));
);
app.use(cors(corsOptions)); app.use(cors(corsOptions));
configurePassport(app);
app.use('/', router); app.use('/', router);
webUIHandler(app); webUIHandler(app);
@@ -63,8 +47,6 @@ app.use(function (req, res, next) {
next(createError(404)); next(createError(404));
}); });
Sentry.attachErrorHandler(app);
app.use(errorHandler); app.use(errorHandler);
export default app; export default app;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,36 +0,0 @@
import path from 'node:path';
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Create folder',
key: 'createFolder',
description: 'Create a new folder with the given parent folder and folder name',
arguments: [
{
label: 'Folder',
key: 'parentFolder',
type: 'string' as const,
required: true,
description: 'Enter the parent folder path, like /TextFiles/ or /Documents/Taxes/',
variables: true,
},
{
label: 'Folder Name',
key: 'folderName',
type: 'string' as const,
required: true,
description: 'Enter the name for the new folder',
variables: true,
},
],
async run($) {
const parentFolder = $.step.parameters.parentFolder as string;
const folderName = $.step.parameters.folderName as string;
const folderPath = path.join(parentFolder, folderName);
const response = await $.http.post('/2/files/create_folder_v2', { path: folderPath });
$.setActionItem({ raw: response.data });
},
});

View File

@@ -1,4 +0,0 @@
import createFolder from "./create-folder";
import renameFile from "./rename-file";
export default [createFolder, renameFile];

View File

@@ -1,45 +0,0 @@
import path from 'node:path';
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'Rename file',
key: 'renameFile',
description: 'Rename a file with the given file path and new name',
arguments: [
{
label: 'File Path',
key: 'filePath',
type: 'string' as const,
required: true,
description:
'Write the full path to the file such as /Folder1/File.pdf',
variables: true,
},
{
label: 'New Name',
key: 'newName',
type: 'string' as const,
required: true,
description: "Enter the new name for the file (without the extension, e.g., '.pdf')",
variables: true,
},
],
async run($) {
const filePath = $.step.parameters.filePath as string;
const newName = $.step.parameters.newName as string;
const fileObject = path.parse(filePath);
const newPath = path.format({
dir: fileObject.dir,
ext: fileObject.ext,
name: newName,
});
const response = await $.http.post('/2/files/move_v2', {
from_path: filePath,
to_path: newPath,
});
$.setActionItem({ raw: response.data.metadata });
},
});

View File

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-label="Dropbox" role="img" viewBox="0 0 512 512" fill="#0061ff">
<path d="M158 101l-99 63 295 188 99-63m-99-188l99 63-295 188-99-63m99 83l98 63 98-63-98-62z"/>
</svg>

Before

Width:  |  Height:  |  Size: 213 B

View File

@@ -1,22 +0,0 @@
import { URLSearchParams } from 'url';
import { IField, IGlobalVariable } from '@automatisch/types';
import scopes from '../common/scopes';
export default async function generateAuthUrl($: IGlobalVariable) {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const callbackUrl = oauthRedirectUrlField.value as string;
const searchParams = new URLSearchParams({
client_id: $.auth.data.clientId as string,
redirect_uri: callbackUrl,
response_type: 'code',
scope: scopes.join(' '),
token_access_type: 'offline',
});
const url = `${$.app.baseUrl}/oauth2/authorize?${searchParams.toString()}`;
await $.auth.set({ url });
}

View File

@@ -1,48 +0,0 @@
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/dropbox/connections/add',
placeholder: null,
description:
'When asked to input an OAuth callback or redirect URL in Dropbox OAuth, enter the URL above.',
clickToCopy: true,
},
{
key: 'clientId',
label: 'App Key',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
{
key: 'clientSecret',
label: 'App Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false,
},
],
generateAuthUrl,
verifyCredentials,
isStillVerified,
refreshToken,
};

View File

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

View File

@@ -1,41 +0,0 @@
import { Buffer } from 'node:buffer';
import { IGlobalVariable } from '@automatisch/types';
const refreshToken = async ($: IGlobalVariable) => {
const params = {
grant_type: 'refresh_token',
refresh_token: $.auth.data.refreshToken as string,
};
const basicAuthToken = Buffer
.from(`${$.auth.data.clientId}:${$.auth.data.clientSecret}`)
.toString('base64');
const { data } = await $.http.post(
'oauth2/token',
null,
{
params,
headers: {
Authorization: `Basic ${basicAuthToken}`
},
additionalProperties: {
skipAddingAuthHeader: true
}
}
);
const {
access_token: accessToken,
expires_in: expiresIn,
token_type: tokenType,
} = data;
await $.auth.set({
accessToken,
expiresIn,
tokenType,
});
};
export default refreshToken;

View File

@@ -1,102 +0,0 @@
import { IGlobalVariable, IField } from '@automatisch/types';
import getCurrentAccount from '../common/get-current-account';
type TAccount = {
account_id: string,
name: {
given_name: string,
surname: string,
familiar_name: string,
display_name: string,
abbreviated_name: string,
},
email: string,
email_verified: boolean,
disabled: boolean,
country: string,
locale: string,
referral_link: string,
is_paired: boolean,
account_type: {
".tag": string,
},
root_info: {
".tag": string,
root_namespace_id: string,
home_namespace_id: string,
},
}
const verifyCredentials = async ($: IGlobalVariable) => {
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUrl = oauthRedirectUrlField.value as string;
const params = {
client_id: $.auth.data.clientId as string,
redirect_uri: redirectUrl,
client_secret: $.auth.data.clientSecret as string,
code: $.auth.data.code as string,
grant_type: 'authorization_code',
}
const { data: verifiedCredentials } = await $.http.post(
'/oauth2/token',
null,
{ params }
);
const {
access_token: accessToken,
refresh_token: refreshToken,
expires_in: expiresIn,
scope: scope,
token_type: tokenType,
account_id: accountId,
team_id: teamId,
id_token: idToken,
uid,
} = verifiedCredentials;
await $.auth.set({
accessToken,
refreshToken,
expiresIn,
scope,
tokenType,
accountId,
teamId,
idToken,
uid
});
const account = await getCurrentAccount($) as TAccount;
await $.auth.set({
accountId: account.account_id,
name: {
givenName: account.name.given_name,
surname: account.name.surname,
familiarName: account.name.familiar_name,
displayName: account.name.display_name,
abbreviatedName: account.name.abbreviated_name,
},
email: account.email,
emailVerified: account.email_verified,
disabled: account.disabled,
country: account.country,
locale: account.locale,
referralLink: account.referral_link,
isPaired: account.is_paired,
accountType: {
".tag": account.account_type['.tag'],
},
rootInfo: {
".tag": account.root_info['.tag'],
rootNamespaceId: account.root_info.root_namespace_id,
homeNamespaceId: account.root_info.home_namespace_id,
},
screenName: `${account.name.display_name} - ${account.email}`,
});
};
export default verifyCredentials;

View File

@@ -1,13 +0,0 @@
import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
requestConfig.headers['Content-Type'] = 'application/json';
if (!requestConfig.additionalProperties?.skipAddingAuthHeader && $.auth.data?.accessToken) {
requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessToken}`;
}
return requestConfig;
};
export default addAuthHeader;

View File

@@ -1,8 +0,0 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
const getCurrentAccount = async ($: IGlobalVariable): Promise<IJSONObject> => {
const response = await $.http.post('/2/users/get_current_account', null);
return response.data;
};
export default getCurrentAccount;

View File

@@ -1,8 +0,0 @@
const scopes = [
'account_info.read',
'files.metadata.read',
'files.content.write',
'files.content.read',
];
export default scopes;

View File

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

View File

@@ -1,109 +0,0 @@
import defineAction from '../../../../helpers/define-action';
type TGroupItem = {
key: string;
operator: keyof TOperators;
value: string;
id: string;
}
type TGroup = Record<'and', TGroupItem[]>;
const isEqual = (a: string, b: string) => 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);
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 = {
equal: TOperatorFunc;
not_equal: TOperatorFunc;
greater_than: TOperatorFunc;
less_than: TOperatorFunc;
greater_than_or_equal: TOperatorFunc;
less_than_or_equal: TOperatorFunc;
contains: TOperatorFunc;
not_contains: TOperatorFunc;
};
const operators: TOperators = {
'equal': isEqual,
'not_equal': isNotEqual,
'greater_than': isGreaterThan,
'less_than': isLessThan,
'greater_than_or_equal': isGreaterThanOrEqual,
'less_than_or_equal': isLessThanOrEqual,
'contains': contains,
'not_contains': doesNotContain,
};
const operate = (operation: keyof TOperators, a: string, b: string) => {
return operators[operation](a, b);
};
export default defineAction({
name: 'Continue if conditions match',
key: 'continueIfMatches',
description: 'Let the execution continue if the conditions match',
arguments: [],
async run($) {
const orGroups = $.step.parameters.or as TGroup[];
const matchingGroups = orGroups.reduce((groups, group) => {
const matchingConditions = group.and
.filter((condition) => operate(condition.operator, condition.key, condition.value));
if (matchingConditions.length) {
return groups.concat([{ and: matchingConditions }]);
}
return groups;
}, []);
if (!shouldContinue(orGroups)) {
$.execution.exit();
}
$.setActionItem({
raw: {
or: matchingGroups,
}
});
},
});

View File

@@ -1,3 +0,0 @@
import continueIfMatches from './continue';
export default [continueIfMatches];

View File

@@ -1,8 +0,0 @@
<svg width="800px" height="800px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Shape" fill="#000000" transform="translate(42.666667, 85.333333)">
<path d="M3.55271368e-14,1.42108547e-14 L191.565013,234.666667 L192,234.666667 L192,384 L234.666667,384 L234.666667,234.666667 L426.666667,1.42108547e-14 L3.55271368e-14,1.42108547e-14 Z M214.448,192 L211.81248,192 L89.9076267,42.6666667 L336.630187,42.6666667 L214.448,192 Z">
</path>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 628 B

View File

@@ -1,14 +0,0 @@
import defineApp from '../../helpers/define-app';
import actions from './actions';
export default defineApp({
name: 'Filter',
key: 'filter',
iconUrl: '{BASE_URL}/apps/filter/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/filter/connection',
supportsConnections: false,
baseUrl: '',
apiBaseUrl: '',
primaryColor: '001F52',
actions,
});

View File

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

View File

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

View File

@@ -1,64 +0,0 @@
import defineAction from '../../../../helpers/define-action';
import capitalize from './transformers/capitalize';
import htmlToMarkdown from './transformers/html-to-markdown';
import markdownToHtml from './transformers/markdown-to-html';
import useDefaultValue from './transformers/use-default-value';
import extractEmailAddress from './transformers/extract-email-address';
const transformers = {
capitalize,
htmlToMarkdown,
markdownToHtml,
useDefaultValue,
extractEmailAddress,
};
export default defineAction({
name: 'Text',
key: 'text',
description:
'Transform text data to capitalize, extract emails, apply default value, and much more.',
arguments: [
{
label: 'Transform',
key: 'transform',
type: 'dropdown' as const,
required: true,
description: 'Pick a channel to send the message to.',
variables: true,
options: [
{ label: 'Capitalize', value: 'capitalize' },
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
{ label: 'Use Default Value', value: 'useDefaultValue' },
{ label: 'Extract Email Address', value: 'extractEmailAddress' },
],
additionalFields: {
type: 'query',
name: 'getDynamicFields',
arguments: [
{
name: 'key',
value: 'listTransformOptions',
},
{
name: 'parameters.transform',
value: '{parameters.transform}',
},
],
},
},
],
async run($) {
const transformerName = $.step.parameters
.transform as keyof typeof transformers;
const output = transformers[transformerName]($);
$.setActionItem({
raw: {
output,
},
});
},
});

View File

@@ -1,11 +0,0 @@
import { IGlobalVariable } from '@automatisch/types';
import { capitalize as lodashCapitalize } from 'lodash';
const capitalize = ($: IGlobalVariable) => {
const input = $.step.parameters.input as string;
const capitalizedInput = input.replace(/\w+/g, lodashCapitalize);
return capitalizedInput;
};
export default capitalize;

View File

@@ -1,12 +0,0 @@
import { IGlobalVariable } from '@automatisch/types';
const extractEmailAddress = ($: IGlobalVariable) => {
const input = $.step.parameters.input as string;
const emailRegexp =
/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
const email = input.match(emailRegexp);
return email ? email[0] : '';
};
export default extractEmailAddress;

View File

@@ -1,11 +0,0 @@
import { IGlobalVariable } from '@automatisch/types';
import { NodeHtmlMarkdown } from 'node-html-markdown';
const htmlToMarkdown = ($: IGlobalVariable) => {
const input = $.step.parameters.input as string;
const markdown = NodeHtmlMarkdown.translate(input);
return markdown;
};
export default htmlToMarkdown;

View File

@@ -1,13 +0,0 @@
import { IGlobalVariable } from '@automatisch/types';
import showdown from 'showdown';
const converter = new showdown.Converter();
const markdownToHtml = ($: IGlobalVariable) => {
const input = $.step.parameters.input as string;
const html = converter.makeHtml(input);
return html;
};
export default markdownToHtml;

View File

@@ -1,13 +0,0 @@
import { IGlobalVariable } from '@automatisch/types';
const useDefaultValue = ($: IGlobalVariable) => {
const input = $.step.parameters.input as string;
if (input && input.trim().length > 0) {
return input;
}
return $.step.parameters.defaultValue as string;
};
export default useDefaultValue;

View File

@@ -1,3 +0,0 @@
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 4H20M4 12H20M4 20H20M4 8H14M4 16H14" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 243 B

View File

@@ -1,3 +0,0 @@
import listTransformOptions from './list-transform-options';
export default [listTransformOptions];

View File

@@ -1,23 +0,0 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
import capitalize from './options/capitalize';
import htmlToMarkdown from './options/html-to-markdown';
import markdownToHtml from './options/markdown-to-html';
import useDefaultValue from './options/use-default-value';
import extractEmailAddress from './options/extract-email-address';
const options: IJSONObject = {
capitalize,
htmlToMarkdown,
markdownToHtml,
useDefaultValue,
extractEmailAddress,
};
export default {
name: 'List fields after transform',
key: 'listTransformOptions',
async run($: IGlobalVariable) {
return options[$.step.parameters.transform as string];
},
};

View File

@@ -1,12 +0,0 @@
const capitalize = [
{
label: 'Input',
key: 'input',
type: 'string' as const,
required: true,
description: 'Text that will be capitalized.',
variables: true,
},
];
export default capitalize;

View File

@@ -1,12 +0,0 @@
const extractEmailAddress = [
{
label: 'Input',
key: 'input',
type: 'string' as const,
required: true,
description: 'Text that will be searched for an email address.',
variables: true,
},
];
export default extractEmailAddress;

View File

@@ -1,12 +0,0 @@
const htmlToMarkdown = [
{
label: 'Input',
key: 'input',
type: 'string' as const,
required: true,
description: 'HTML that will be converted to Markdown.',
variables: true,
},
];
export default htmlToMarkdown;

View File

@@ -1,12 +0,0 @@
const markdownToHtml = [
{
label: 'Input',
key: 'input',
type: 'string' as const,
required: true,
description: 'Markdown text that will be converted to HTML.',
variables: true,
},
];
export default markdownToHtml;

View File

@@ -1,21 +0,0 @@
const useDefaultValue = [
{
label: 'Input',
key: 'input',
type: 'string' as const,
required: true,
description: 'Text you want to check whether it is empty or not.',
variables: true,
},
{
label: 'Default Value',
key: 'defaultValue',
type: 'string' as const,
required: true,
description:
'Text that will be used as a default value if the input is empty.',
variables: true,
},
];
export default useDefaultValue;

View File

@@ -1,16 +0,0 @@
import defineApp from '../../helpers/define-app';
import actions from './actions';
import dynamicFields from './dynamic-fields';
export default defineApp({
name: 'Formatter',
key: 'formatter',
iconUrl: '{BASE_URL}/apps/formatter/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/formatter/connection',
supportsConnections: false,
baseUrl: '',
apiBaseUrl: '',
primaryColor: '001F52',
actions,
dynamicFields,
});

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
<!-- 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>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,24 +0,0 @@
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

@@ -1,63 +0,0 @@
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

@@ -1,9 +0,0 @@
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

@@ -1,24 +0,0 @@
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

@@ -1,47 +0,0 @@
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($);
const screenName = [
currentUser.username,
$.auth.data.instanceUrl,
]
.filter(Boolean)
.join(' @ ');
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,
});
};
export default verifyCredentials;

View File

@@ -1,11 +0,0 @@
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

@@ -1,15 +0,0 @@
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

@@ -1,11 +0,0 @@
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

@@ -1,33 +0,0 @@
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

@@ -1,13 +0,0 @@
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

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

View File

@@ -1,33 +0,0 @@
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

@@ -1 +0,0 @@

View File

@@ -1,21 +0,0 @@
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

@@ -1,27 +0,0 @@
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

@@ -1,159 +0,0 @@
// 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

@@ -1,27 +0,0 @@
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

@@ -1,74 +0,0 @@
// 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

@@ -1,45 +0,0 @@
// 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

@@ -1,26 +0,0 @@
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

@@ -1,38 +0,0 @@
// 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

@@ -1,26 +0,0 @@
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

@@ -1,29 +0,0 @@
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

@@ -1,26 +0,0 @@
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

@@ -1,159 +0,0 @@
// 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

@@ -1,25 +0,0 @@
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

@@ -1,60 +0,0 @@
// 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

@@ -1,89 +0,0 @@
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

@@ -1,26 +0,0 @@
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

@@ -1,208 +0,0 @@
// 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

@@ -1,26 +0,0 @@
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

@@ -1,74 +0,0 @@
// 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

@@ -1,26 +0,0 @@
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);

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