Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b347df38b5 | ||
![]() |
5e2b8c6a1e |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,4 +1,3 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,7 @@ You can use `user@automatisch.io` email address and `sample` password to login t
|
|||||||
|
|
||||||
## Community Links
|
## Community Links
|
||||||
|
|
||||||
|
- [Github](https://github.com/automatisch/automatisch)
|
||||||
- [Discord](https://discord.gg/dJSah9CVrC)
|
- [Discord](https://discord.gg/dJSah9CVrC)
|
||||||
- [Twitter](https://twitter.com/automatischio)
|
- [Twitter](https://twitter.com/automatischio)
|
||||||
|
|
||||||
@@ -48,4 +49,4 @@ If you have any questions or problems, please visit our GitHub discussions page,
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Automatisch is an open-source software with the [AGPL 3.0 license](https://github.com/automatisch/automatisch/blob/main/LICENSE.md).
|
Automatisch is an open-source software with an [AGPL 3.0 license](https://github.com/automatisch/automatisch/blob/main/LICENSE.md).
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
version: '3.9'
|
version: "3.9"
|
||||||
services:
|
services:
|
||||||
main:
|
main:
|
||||||
build:
|
build:
|
||||||
context: ../images/main
|
context: ../images/wait-for-postgres
|
||||||
network: host
|
network: host
|
||||||
ports:
|
ports:
|
||||||
- '3000:3000'
|
- "3000:3000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
@@ -22,7 +22,7 @@ services:
|
|||||||
- automatisch_storage:/automatisch/storage
|
- automatisch_storage:/automatisch/storage
|
||||||
worker:
|
worker:
|
||||||
build:
|
build:
|
||||||
context: ../images/worker
|
context: ../images/plain
|
||||||
network: host
|
network: host
|
||||||
depends_on:
|
depends_on:
|
||||||
- main
|
- main
|
||||||
@@ -32,15 +32,16 @@ services:
|
|||||||
- POSTGRES_HOST=postgres
|
- POSTGRES_HOST=postgres
|
||||||
- POSTGRES_DATABASE=automatisch
|
- POSTGRES_DATABASE=automatisch
|
||||||
- POSTGRES_USERNAME=automatisch_user
|
- POSTGRES_USERNAME=automatisch_user
|
||||||
|
command: automatisch start-worker --env-file /automatisch/storage/.env
|
||||||
volumes:
|
volumes:
|
||||||
- automatisch_storage:/automatisch/storage
|
- automatisch_storage:/automatisch/storage
|
||||||
postgres:
|
postgres:
|
||||||
image: 'postgres:14.5'
|
image: "postgres:14.5"
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
POSTGRES_DB: automatisch
|
POSTGRES_DB: automatisch
|
||||||
POSTGRES_USER: automatisch_user
|
POSTGRES_USER: automatisch_user
|
||||||
redis:
|
redis:
|
||||||
image: 'redis:7.0.4'
|
image: "redis:7.0.4"
|
||||||
volumes:
|
volumes:
|
||||||
automatisch_storage:
|
automatisch_storage:
|
||||||
|
5
docker/images/plain/Dockerfile
Normal file
5
docker/images/plain/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM node:16
|
||||||
|
WORKDIR /automatisch
|
||||||
|
|
||||||
|
RUN yarn global add @automatisch/cli@0.1.5
|
@@ -3,7 +3,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
until psql -h "$POSTGRES_HOST" -U "$POSTGRES_USERNAME" -d "$POSTGRES_HOST" -c '\q'; do
|
until psql -h "$POSTGRES_HOST" -U "$POSTGRES_USERNAME" -d "$POSTGRES_HOST" -c '\q'; do
|
||||||
>&2 echo "Waiting for Postgres to be ready..."
|
>&2 echo "Postgres is unavailable - sleeping"
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
@@ -1,10 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1
|
|
||||||
FROM node:16
|
|
||||||
WORKDIR /automatisch
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y postgresql-client
|
|
||||||
|
|
||||||
COPY ./wait-for-postgres.sh /automatisch/wait-for-postgres.sh
|
|
||||||
|
|
||||||
RUN yarn global add @automatisch/cli@0.1.5
|
|
||||||
CMD sh /automatisch/wait-for-postgres.sh automatisch start-worker --env-file /automatisch/storage/.env
|
|
@@ -1,11 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
until psql -h "$POSTGRES_HOST" -U "$POSTGRES_USERNAME" -d "$POSTGRES_HOST" -c '\q'; do
|
|
||||||
>&2 echo "Waiting for Postgres to be ready..."
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
>&2 echo "Postgres is up - executing command"
|
|
||||||
exec "$@"
|
|
@@ -2,7 +2,7 @@
|
|||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"version": "0.2.0",
|
"version": "0.1.5",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"command": {
|
"command": {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
HOST=localhost
|
HOST=localhost
|
||||||
PROTOCOL=http
|
PROTOCOL=http
|
||||||
PORT=3000
|
PORT=3000
|
||||||
WEB_APP_URL=http://localhost:3001
|
WEB_APP_URL=https://localhost:3001
|
||||||
APP_ENV=development
|
APP_ENV=development
|
||||||
POSTGRES_DATABASE=automatisch_development
|
POSTGRES_DATABASE=automatisch_development
|
||||||
POSTGRES_PORT=5432
|
POSTGRES_PORT=5432
|
||||||
@@ -14,4 +14,3 @@ APP_SECRET_KEY=sample-app-secret-key
|
|||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
REDIS_HOST=127.0.0.1
|
REDIS_HOST=127.0.0.1
|
||||||
ENABLE_BULLMQ_DASHBOARD=false
|
ENABLE_BULLMQ_DASHBOARD=false
|
||||||
SERVE_WEB_APP_SEPARATELY=true
|
|
||||||
|
@@ -4,6 +4,6 @@ const client = new Client({
|
|||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
user: 'postgres',
|
user: 'postgres',
|
||||||
port: 5432,
|
port: 5432,
|
||||||
});
|
})
|
||||||
|
|
||||||
export default client;
|
export default client;
|
||||||
|
@@ -4,10 +4,7 @@ import client from './client';
|
|||||||
import User from '../../src/models/user';
|
import User from '../../src/models/user';
|
||||||
import '../../src/config/orm';
|
import '../../src/config/orm';
|
||||||
|
|
||||||
export async function createUser(
|
export async function createUser(email = 'user@automatisch.io', password = 'sample') {
|
||||||
email = 'user@automatisch.io',
|
|
||||||
password = 'sample'
|
|
||||||
) {
|
|
||||||
const UNIQUE_VIOLATION_CODE = '23505';
|
const UNIQUE_VIOLATION_CODE = '23505';
|
||||||
const userParams = {
|
const userParams = {
|
||||||
email,
|
email,
|
||||||
@@ -32,17 +29,14 @@ export async function createUser(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createDatabaseAndUser = async (
|
export const createDatabaseAndUser = async (database = appConfig.postgresDatabase, user = appConfig.postgresUsername) => {
|
||||||
database = appConfig.postgresDatabase,
|
|
||||||
user = appConfig.postgresUsername
|
|
||||||
) => {
|
|
||||||
await client.connect();
|
await client.connect();
|
||||||
await createDatabase(database);
|
await createDatabase(database);
|
||||||
await createDatabaseUser(user);
|
await createDatabaseUser(user);
|
||||||
await grantPrivileges(database, user);
|
await grantPrivileges(database, user);
|
||||||
|
|
||||||
await client.end();
|
await client.end();
|
||||||
};
|
}
|
||||||
|
|
||||||
export const createDatabase = async (database = appConfig.postgresDatabase) => {
|
export const createDatabase = async (database = appConfig.postgresDatabase) => {
|
||||||
const DUPLICATE_DB_CODE = '42P04';
|
const DUPLICATE_DB_CODE = '42P04';
|
||||||
@@ -57,7 +51,7 @@ export const createDatabase = async (database = appConfig.postgresDatabase) => {
|
|||||||
|
|
||||||
logger.info(`Database: ${database} already exists!`);
|
logger.info(`Database: ${database} already exists!`);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const createDatabaseUser = async (user = appConfig.postgresUsername) => {
|
export const createDatabaseUser = async (user = appConfig.postgresUsername) => {
|
||||||
const DUPLICATE_OBJECT_CODE = '42710';
|
const DUPLICATE_OBJECT_CODE = '42710';
|
||||||
@@ -74,25 +68,25 @@ export const createDatabaseUser = async (user = appConfig.postgresUsername) => {
|
|||||||
|
|
||||||
logger.info(`Database User: ${user} already exists!`);
|
logger.info(`Database User: ${user} already exists!`);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const grantPrivileges = async (
|
export const grantPrivileges = async (
|
||||||
database = appConfig.postgresDatabase,
|
database = appConfig.postgresDatabase, user = appConfig.postgresUsername
|
||||||
user = appConfig.postgresUsername
|
|
||||||
) => {
|
) => {
|
||||||
await client.query(
|
await client.query(
|
||||||
`GRANT ALL PRIVILEGES ON DATABASE ${database} TO ${user};`
|
`GRANT ALL PRIVILEGES ON DATABASE ${database} TO ${user};`
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.info(`${user} has granted all privileges on ${database}!`);
|
logger.info(
|
||||||
};
|
`${user} has granted all privileges on ${database}!`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export const dropDatabase = async () => {
|
export const dropDatabase = async () => {
|
||||||
if (appConfig.appEnv != 'development' && appConfig.appEnv != 'test') {
|
if (appConfig.appEnv != 'development' && appConfig.appEnv != 'test') {
|
||||||
const errorMessage =
|
const errorMessage = 'Drop database command can be used only with development or test environments!'
|
||||||
'Drop database command can be used only with development or test environments!';
|
|
||||||
|
|
||||||
logger.error(errorMessage);
|
logger.error(errorMessage)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,15 +94,13 @@ export const dropDatabase = async () => {
|
|||||||
await dropDatabaseAndUser();
|
await dropDatabaseAndUser();
|
||||||
|
|
||||||
await client.end();
|
await client.end();
|
||||||
};
|
}
|
||||||
|
|
||||||
export const dropDatabaseAndUser = async (
|
export const dropDatabaseAndUser = async(database = appConfig.postgresDatabase, user = appConfig.postgresUsername) => {
|
||||||
database = appConfig.postgresDatabase,
|
|
||||||
user = appConfig.postgresUsername
|
|
||||||
) => {
|
|
||||||
await client.query(`DROP DATABASE IF EXISTS ${database}`);
|
await client.query(`DROP DATABASE IF EXISTS ${database}`);
|
||||||
logger.info(`Database: ${database} removed!`);
|
logger.info(`Database: ${database} removed!`);
|
||||||
|
|
||||||
await client.query(`DROP USER IF EXISTS ${user}`);
|
await client.query(`DROP USER IF EXISTS ${user}`);
|
||||||
logger.info(`Database User: ${user} removed!`);
|
logger.info(`Database User: ${user} removed!`);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ const knexConfig = {
|
|||||||
user: appConfig.postgresUsername,
|
user: appConfig.postgresUsername,
|
||||||
password: appConfig.postgresPassword,
|
password: appConfig.postgresPassword,
|
||||||
database: appConfig.postgresDatabase,
|
database: appConfig.postgresDatabase,
|
||||||
ssl: appConfig.postgresEnableSsl,
|
ssl: appConfig.postgresEnableSsl
|
||||||
},
|
},
|
||||||
pool: { min: 0, max: 20 },
|
pool: { min: 0, max: 20 },
|
||||||
migrations: {
|
migrations: {
|
||||||
@@ -20,7 +20,7 @@ const knexConfig = {
|
|||||||
},
|
},
|
||||||
seeds: {
|
seeds: {
|
||||||
directory: __dirname + '/src/db/seeds',
|
directory: __dirname + '/src/db/seeds',
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default knexConfig;
|
export default knexConfig;
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@automatisch/backend",
|
"name": "@automatisch/backend",
|
||||||
"version": "0.2.0",
|
"version": "0.1.5",
|
||||||
"license": "AGPL-3.0",
|
"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 --exit-child src/server.ts",
|
"dev": "ts-node-dev 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",
|
||||||
@@ -22,25 +22,28 @@
|
|||||||
"prebuild": "rm -rf ./dist"
|
"prebuild": "rm -rf ./dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@automatisch/web": "^0.2.0",
|
"@automatisch/web": "^0.1.5",
|
||||||
"@bull-board/express": "^3.10.1",
|
"@bull-board/express": "^3.10.1",
|
||||||
|
"@gitbeaker/node": "^35.6.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",
|
||||||
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
||||||
|
"@slack/bolt": "3.10.0",
|
||||||
"@types/luxon": "^2.3.1",
|
"@types/luxon": "^2.3.1",
|
||||||
"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",
|
||||||
"bullmq": "^3.0.0",
|
"bullmq": "^1.76.1",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"debug": "~2.6.9",
|
"debug": "~2.6.9",
|
||||||
|
"discord.js": "13.2.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"express": "~4.16.1",
|
"express": "~4.16.1",
|
||||||
"express-basic-auth": "^1.2.1",
|
|
||||||
"express-graphql": "^0.12.0",
|
"express-graphql": "^0.12.0",
|
||||||
"fast-xml-parser": "^4.0.11",
|
"flickr-sdk": "3.10.0",
|
||||||
|
"googleapis": "89.0.0",
|
||||||
"graphql-middleware": "^6.1.15",
|
"graphql-middleware": "^6.1.15",
|
||||||
"graphql-shield": "^7.5.0",
|
"graphql-shield": "^7.5.0",
|
||||||
"graphql-tools": "^8.2.0",
|
"graphql-tools": "^8.2.0",
|
||||||
@@ -54,7 +57,11 @@
|
|||||||
"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",
|
||||||
|
"octokit": "^1.7.1",
|
||||||
"pg": "^8.7.1",
|
"pg": "^8.7.1",
|
||||||
|
"twilio": "3.70.0",
|
||||||
|
"twitch-js": "2.0.0-beta.42",
|
||||||
|
"twitter-api-v2": "1.6.0",
|
||||||
"winston": "^3.7.1"
|
"winston": "^3.7.1"
|
||||||
},
|
},
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -93,7 +100,7 @@
|
|||||||
"url": "https://github.com/automatisch/automatisch/issues"
|
"url": "https://github.com/automatisch/automatisch/issues"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@automatisch/types": "^0.2.0",
|
"@automatisch/types": "^0.1.5",
|
||||||
"@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",
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import appConfig from './config/app';
|
||||||
import createError from 'http-errors';
|
import createError from 'http-errors';
|
||||||
import express, { Request, Response, NextFunction } from 'express';
|
import express, { Request, Response, NextFunction } from 'express';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
@@ -14,11 +15,15 @@ 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';
|
||||||
|
|
||||||
|
if (appConfig.enableBullMQDashboard) {
|
||||||
createBullBoardHandler(serverAdapter);
|
createBullBoardHandler(serverAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
if (appConfig.enableBullMQDashboard) {
|
||||||
injectBullBoardHandler(app, serverAdapter);
|
injectBullBoardHandler(app, serverAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
appAssetsHandler(app);
|
appAssetsHandler(app);
|
||||||
|
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
import translateText from './translate-text';
|
|
||||||
|
|
||||||
export default [translateText];
|
|
@@ -1,77 +0,0 @@
|
|||||||
import qs from 'qs';
|
|
||||||
import defineAction from '../../../../helpers/define-action';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Translate Text',
|
|
||||||
key: 'translateText',
|
|
||||||
description: 'Translates text from one language to another.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Text',
|
|
||||||
key: 'text',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
description: 'Text to be translated.',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Target Language',
|
|
||||||
key: 'targetLanguage',
|
|
||||||
type: 'dropdown' as const,
|
|
||||||
required: true,
|
|
||||||
description: 'Language to translate the text to.',
|
|
||||||
variables: false,
|
|
||||||
value: '',
|
|
||||||
options: [
|
|
||||||
{ label: 'Bulgarian', value: 'BG' },
|
|
||||||
{ label: 'Chinese (simplified)', value: 'ZH' },
|
|
||||||
{ label: 'Czech', value: 'CS' },
|
|
||||||
{ label: 'Danish', value: 'DA' },
|
|
||||||
{ label: 'Dutch', value: 'NL' },
|
|
||||||
{ label: 'English', value: 'EN' },
|
|
||||||
{ label: 'English (American)', value: 'EN-US' },
|
|
||||||
{ label: 'English (British)', value: 'EN-GB' },
|
|
||||||
{ label: 'Estonian', value: 'ET' },
|
|
||||||
{ label: 'Finnish', value: 'FI' },
|
|
||||||
{ label: 'French', value: 'FR' },
|
|
||||||
{ label: 'German', value: 'DE' },
|
|
||||||
{ label: 'Greek', value: 'EL' },
|
|
||||||
{ label: 'Hungarian', value: 'HU' },
|
|
||||||
{ label: 'Indonesian', value: 'ID' },
|
|
||||||
{ label: 'Italian', value: 'IT' },
|
|
||||||
{ label: 'Japanese', value: 'JA' },
|
|
||||||
{ label: 'Latvian', value: 'LV' },
|
|
||||||
{ label: 'Lithuanian', value: 'LT' },
|
|
||||||
{ label: 'Polish', value: 'PL' },
|
|
||||||
{ label: 'Portuguese', value: 'PT' },
|
|
||||||
{ label: 'Portuguese (Brazilian)', value: 'PT-BR' },
|
|
||||||
{
|
|
||||||
label:
|
|
||||||
'Portuguese (all Portuguese varieties excluding Brazilian Portuguese)',
|
|
||||||
value: 'PT-PT',
|
|
||||||
},
|
|
||||||
{ label: 'Romanian', value: 'RO' },
|
|
||||||
{ label: 'Russian', value: 'RU' },
|
|
||||||
{ label: 'Slovak', value: 'SK' },
|
|
||||||
{ label: 'Slovenian', value: 'SL' },
|
|
||||||
{ label: 'Spanish', value: 'ES' },
|
|
||||||
{ label: 'Swedish', value: 'SV' },
|
|
||||||
{ label: 'Turkish', value: 'TR' },
|
|
||||||
{ label: 'Ukrainian', value: 'UK' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const stringifiedBody = qs.stringify({
|
|
||||||
text: $.step.parameters.text,
|
|
||||||
target_lang: $.step.parameters.targetLanguage,
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await $.http.post('/v2/translate', stringifiedBody);
|
|
||||||
|
|
||||||
$.setActionItem({
|
|
||||||
raw: response.data,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,39 +0,0 @@
|
|||||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<metadata id="metadata4177">image/svg+xml</metadata>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clipPath4187" clipPathUnits="userSpaceOnUse">
|
|
||||||
<path id="path4189" d="m70.850157,393.069981l708.661,0l0,-425.197l-708.661,0l0,425.197z"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
<g>
|
|
||||||
<title>background</title>
|
|
||||||
<rect fill="#ffffff" id="canvas_background" height="130" width="130" y="-1" x="-1"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<title>Layer 1</title>
|
|
||||||
<g fill="#FFF" transform="matrix(1.36265596065192,0,0,-1.3395561298863794,-403.63320244005325,311.0674198374575) " id="g4179">
|
|
||||||
<g fill="#FFF" id="g4183">
|
|
||||||
<g fill="#FFF" clip-path="url(#clipPath4187)" id="g4185">
|
|
||||||
<g fill="#FFF" id="g4191">
|
|
||||||
<path fill="#000000" fill-rule="nonzero" id="path4193" d="m411.83557,185.452083c0,-2.425 -0.463,-4.637 -1.3,-6.226c-1.009,-1.905 -2.173,-3.234 -3.366,-3.843c-1.641,-0.835 -3.707,-1.69 -6.586,-1.69l-4.044,0l0,22.753l3.221,0c4.28,0 7.213,-0.841 8.971,-2.572c2.09,-2.059 3.104,-4.814 3.104,-8.422m-1.89,15.206c-2.434,1.24 -6.053,1.24 -10.633,1.24l-7.069,0c-1.158,0 -2.097,-0.939 -2.097,-2.097l0,-29.463c0,-1.157 0.939,-2.096 2.097,-2.096l6.662,0c3.78,0 7.209,0.393 9.411,1.079c1.585,0.491 3.181,1.529 5.022,3.27c1.559,1.472 2.785,3.317 3.642,5.484c0.886,2.243 1.318,4.609 1.318,7.232c0,3.66 -0.771,6.809 -2.359,9.622c-1.441,2.558 -3.344,4.377 -5.994,5.729"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#FFF" id="g4195">
|
|
||||||
<path fill="#000000" fill-rule="nonzero" id="path4197" d="m533.882353,173.693278l-10.436,0l0,27.36c0,0.467 -0.38,0.845 -0.845,0.845l-4.702,0c-0.466,0 -0.845,-0.378 -0.845,-0.845l0,-31.966c0,-0.467 0.379,-0.846 0.845,-0.846l15.983,0c0.465,0 0.848,0.379 0.848,0.846l0,3.759c0,0.469 -0.383,0.847 -0.848,0.847"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#FFF" id="g4199">
|
|
||||||
<path fill="#000000" fill-rule="nonzero" id="path4201" d="m506.182158,181.465785c0,-2.225 -0.685,-4.08 -2.034,-5.516c-1.35,-1.437 -3.039,-2.167 -5.017,-2.167c-2.038,0 -3.751,0.718 -5.096,2.13c-1.34,1.415 -2.022,3.291 -2.022,5.576c0,2.242 0.682,4.081 2.024,5.471c1.343,1.391 3.056,2.096 5.094,2.096c2.026,0 3.724,-0.716 5.05,-2.132c1.326,-1.412 2.001,-3.249 2.001,-5.458m-5.813,13.324c-1.536,0 -3.091,-0.305 -4.385,-0.862c-1.508,-0.646 -2.894,-1.553 -3.903,-2.554l-0.601,-0.48l-2.708,2.543l-2.843,0c-0.465,0 -0.842,-0.377 -0.842,-0.842l0,-32.911c0,-0.467 0.378,-0.845 0.845,-0.845l4.701,0c0.467,0 0.847,0.379 0.847,0.847l0,12.166l0.598,-0.455c1.287,-1.204 2.95,-2.034 4.125,-2.52c1.249,-0.516 2.621,-0.78 4.076,-0.78c3.306,0 6.085,1.248 8.499,3.812c2.408,2.557 3.581,5.661 3.581,9.488c0,3.886 -1.176,7.13 -3.505,9.64c-2.302,2.491 -5.157,3.753 -8.485,3.753"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#FFF" id="g4203">
|
|
||||||
<path fill="#000000" fill-rule="nonzero" id="path4205" d="m460.078642,184.036586l0.149,0.387c0.847,1.866 1.627,3.045 2.381,3.604c1.364,1.009 2.965,1.522 4.765,1.522c1.543,0 3.024,-0.499 4.396,-1.485c1.209,-0.865 2.09,-2.13 2.533,-3.616l0.101,-0.412l-14.325,0zm6.978,10.753c-3.801,0 -6.871,-1.255 -9.39,-3.836c-2.52,-2.584 -3.745,-5.741 -3.745,-9.651c0,-3.815 1.228,-6.902 3.757,-9.439c2.526,-2.535 5.709,-3.766 9.73,-3.766c2.584,0 4.824,0.454 6.656,1.348c1.543,0.752 2.915,1.843 4.079,3.243c0.167,0.203 0.233,0.472 0.172,0.728c-0.056,0.257 -0.23,0.472 -0.469,0.583l-3.782,1.739c-0.3,0.138 -0.656,0.089 -0.907,-0.128c-1.691,-1.459 -3.641,-2.2 -5.797,-2.2c-2.156,0 -3.793,0.489 -5.008,1.493c-1.29,1.072 -2.003,2.191 -2.332,3.679l19.696,0c0.466,0 0.842,0.375 0.844,0.841l0.007,0.835c0,4.359 -1.369,8.256 -3.757,10.695c-2.529,2.581 -5.718,3.836 -9.754,3.836"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#FFF" id="g4207">
|
|
||||||
<path fill="#000000" fill-rule="nonzero" id="path4209" d="m428.942961,184.036586l0.148,0.386c0.854,1.871 1.635,3.05 2.387,3.605c1.36,1.009 2.963,1.522 4.763,1.522c1.542,0 3.022,-0.5 4.396,-1.485c1.208,-0.865 2.088,-2.13 2.533,-3.615l0.099,-0.413l-14.326,0zm6.979,10.753c-3.798,0 -6.871,-1.255 -9.39,-3.836c-2.521,-2.584 -3.745,-5.741 -3.745,-9.651c0,-3.816 1.227,-6.904 3.757,-9.439c2.526,-2.535 5.709,-3.766 9.73,-3.766c2.584,0 4.824,0.454 6.656,1.348c1.544,0.752 2.915,1.843 4.079,3.244c0.168,0.202 0.231,0.471 0.172,0.728c-0.056,0.256 -0.231,0.471 -0.469,0.582l-3.782,1.739c-0.298,0.138 -0.655,0.089 -0.907,-0.128c-1.688,-1.459 -3.639,-2.2 -5.797,-2.2c-2.156,0 -3.793,0.489 -5.005,1.494c-1.29,1.071 -2.006,2.189 -2.335,3.678l19.696,0c0.465,0 0.844,0.375 0.847,0.841l0.004,0.835c0,4.358 -1.369,8.254 -3.757,10.695c-2.526,2.581 -5.715,3.836 -9.754,3.836"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#FFF" id="g4211">
|
|
||||||
<path fill="#000000" fill-rule="nonzero" id="path4213" d="m355.595244,180.065883c-2.797,0 -5.063,2.266 -5.063,5.063c0,0.295 0.025,0.584 0.075,0.865l-12.108,6.972c-0.881,-0.744 -2.02,-1.193 -3.263,-1.193c-2.797,0 -5.064,2.267 -5.064,5.063c0,2.796 2.267,5.063 5.064,5.063c2.797,0 5.064,-2.267 5.064,-5.063c0,-0.322 -0.033,-0.637 -0.091,-0.943l12.064,-6.946c0.889,0.775 2.051,1.245 3.322,1.245c2.795,0 5.062,-2.267 5.062,-5.063c0,-2.797 -2.267,-5.063 -5.062,-5.063m-15.357,-6.724c0,-2.796 -2.266,-5.063 -5.063,-5.063c-2.795,0 -5.062,2.267 -5.062,5.063c0,2.797 2.267,5.064 5.062,5.064c1.257,0 2.405,-0.459 3.29,-1.217l8.827,5.073c0.377,-1.089 0.963,-2.081 1.708,-2.929l-8.843,-5.083c0.052,-0.295 0.081,-0.598 0.081,-0.908m30.444,30.899l-24.042,13.738c-1.637,0.935 -3.647,0.935 -5.284,0l-24.04,-13.738c-1.66,-0.948 -2.684,-2.714 -2.684,-4.624l0,-27.946c0,-1.902 1.015,-3.661 2.663,-4.612l41.387,-23.889l0.007,16.968l12.01,6.921c1.65,0.951 2.666,2.71 2.666,4.614l0,27.944c0,1.91 -1.024,3.676 -2.683,4.624"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.4 KiB |
@@ -1,33 +0,0 @@
|
|||||||
import verifyCredentials from './verify-credentials';
|
|
||||||
import isStillVerified from './is-still-verified';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
key: 'screenName',
|
|
||||||
label: 'Screen Name',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description:
|
|
||||||
'Screen name of your connection to be used on Automatisch UI.',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'authenticationKey',
|
|
||||||
label: 'Authentication Key',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: 'DeepL authentication key of your account.',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
};
|
|
@@ -1,9 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
import verifyCredentials from './verify-credentials';
|
|
||||||
|
|
||||||
const isStillVerified = async ($: IGlobalVariable) => {
|
|
||||||
await verifyCredentials($);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default isStillVerified;
|
|
@@ -1,11 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
|
|
||||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
|
||||||
await $.http.get('/v2/usage');
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
screenName: $.auth.data.screenName,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
@@ -1,12 +0,0 @@
|
|||||||
import { TBeforeRequest } from '@automatisch/types';
|
|
||||||
|
|
||||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
|
||||||
if ($.auth.data?.authenticationKey) {
|
|
||||||
const authorizationHeader = `DeepL-Auth-Key ${$.auth.data.authenticationKey}`;
|
|
||||||
requestConfig.headers.Authorization = authorizationHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -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: 'DeepL',
|
|
||||||
key: 'deepl',
|
|
||||||
iconUrl: '{BASE_URL}/apps/deepl/assets/favicon.svg',
|
|
||||||
authDocUrl: 'https://automatisch.io/docs/apps/deepl/connection',
|
|
||||||
supportsConnections: true,
|
|
||||||
baseUrl: 'https://deepl.com',
|
|
||||||
apiBaseUrl: 'https://api.deepl.com',
|
|
||||||
primaryColor: '0d2d45',
|
|
||||||
beforeRequest: [addAuthHeader],
|
|
||||||
auth,
|
|
||||||
actions,
|
|
||||||
});
|
|
@@ -1,3 +0,0 @@
|
|||||||
import sendMessageToChannel from './send-message-to-channel';
|
|
||||||
|
|
||||||
export default [sendMessageToChannel];
|
|
@@ -1,47 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Send a message to channel',
|
|
||||||
key: 'sendMessageToChannel',
|
|
||||||
description: 'Sends a message to a specific channel you specify.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Channel',
|
|
||||||
key: 'channel',
|
|
||||||
type: 'dropdown' as const,
|
|
||||||
required: true,
|
|
||||||
description: 'Pick a channel to send the message to.',
|
|
||||||
variables: false,
|
|
||||||
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 data = {
|
|
||||||
content: $.step.parameters.message as string,
|
|
||||||
};
|
|
||||||
const response = await $.http?.post(
|
|
||||||
`/channels/${$.step.parameters.channel}/messages`,
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
$.setActionItem({ raw: response.data });
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,22 +0,0 @@
|
|||||||
import { IField, IGlobalVariable } from '@automatisch/types';
|
|
||||||
import { URLSearchParams } from 'url';
|
|
||||||
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.consumerKey as string,
|
|
||||||
redirect_uri: callbackUrl,
|
|
||||||
response_type: 'code',
|
|
||||||
permissions: '2146958591',
|
|
||||||
scope: scopes.join(' '),
|
|
||||||
});
|
|
||||||
|
|
||||||
const url = `${$.app.apiBaseUrl}/oauth2/authorize?${searchParams.toString()}`;
|
|
||||||
|
|
||||||
await $.auth.set({ url });
|
|
||||||
}
|
|
@@ -1,61 +0,0 @@
|
|||||||
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/discord/connections/add',
|
|
||||||
placeholder: null,
|
|
||||||
description:
|
|
||||||
'When asked to input an OAuth callback or redirect URL in Discord OAuth, enter the URL above.',
|
|
||||||
docUrl: 'https://automatisch.io/docs/discord#oauth-redirect-url',
|
|
||||||
clickToCopy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'consumerKey',
|
|
||||||
label: 'Consumer Key',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
docUrl: 'https://automatisch.io/docs/discord#consumer-key',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'consumerSecret',
|
|
||||||
label: 'Consumer Secret',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
docUrl: 'https://automatisch.io/docs/discord#consumer-secret',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'botToken',
|
|
||||||
label: 'Bot token',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
docUrl: 'https://automatisch.io/docs/discord#bot-token',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
generateAuthUrl,
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
};
|
|
@@ -1,10 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
import getCurrentUser from '../common/get-current-user';
|
|
||||||
|
|
||||||
const isStillVerified = async ($: IGlobalVariable) => {
|
|
||||||
await getCurrentUser($);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default isStillVerified;
|
|
@@ -1,53 +0,0 @@
|
|||||||
import { IGlobalVariable, IField } from '@automatisch/types';
|
|
||||||
import { URLSearchParams } from 'url';
|
|
||||||
import scopes from '../common/scopes';
|
|
||||||
import getCurrentUser from '../common/get-current-user';
|
|
||||||
|
|
||||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
|
||||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
|
||||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
|
||||||
);
|
|
||||||
const callbackUrl = oauthRedirectUrlField.value as string;
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
client_id: $.auth.data.consumerKey as string,
|
|
||||||
redirect_uri: callbackUrl,
|
|
||||||
response_type: 'code',
|
|
||||||
scope: scopes.join(' '),
|
|
||||||
client_secret: $.auth.data.consumerSecret as string,
|
|
||||||
code: $.auth.data.code as string,
|
|
||||||
grant_type: 'authorization_code',
|
|
||||||
});
|
|
||||||
const { data: verifiedCredentials } = await $.http.post(
|
|
||||||
'/oauth2/token',
|
|
||||||
params.toString()
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
|
||||||
access_token: accessToken,
|
|
||||||
refresh_token: refreshToken,
|
|
||||||
expires_in: expiresIn,
|
|
||||||
scope: scope,
|
|
||||||
token_type: tokenType,
|
|
||||||
guild: { id: guildId, name: guildName },
|
|
||||||
} = verifiedCredentials;
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
accessToken,
|
|
||||||
refreshToken,
|
|
||||||
expiresIn,
|
|
||||||
scope,
|
|
||||||
tokenType,
|
|
||||||
});
|
|
||||||
|
|
||||||
const user = await getCurrentUser($);
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
userId: user.id,
|
|
||||||
screenName: user.username,
|
|
||||||
email: user.email,
|
|
||||||
guildId,
|
|
||||||
guildName,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
99
packages/backend/src/apps/discord/authentication.ts
Normal file
99
packages/backend/src/apps/discord/authentication.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import type {
|
||||||
|
IAuthentication,
|
||||||
|
IApp,
|
||||||
|
IField,
|
||||||
|
IJSONObject,
|
||||||
|
} from '@automatisch/types';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
import axios, { AxiosInstance } from 'axios';
|
||||||
|
|
||||||
|
export default class Authentication implements IAuthentication {
|
||||||
|
appData: IApp;
|
||||||
|
connectionData: IJSONObject;
|
||||||
|
client: AxiosInstance = axios.create({
|
||||||
|
baseURL: 'https://discord.com/api/',
|
||||||
|
});
|
||||||
|
|
||||||
|
scope: string[] = ['identify', 'email'];
|
||||||
|
|
||||||
|
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||||
|
this.appData = appData;
|
||||||
|
this.connectionData = connectionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
get oauthRedirectUrl() {
|
||||||
|
return this.appData.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAuthData() {
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
client_id: this.connectionData.consumerKey as string,
|
||||||
|
redirect_uri: this.oauthRedirectUrl,
|
||||||
|
response_type: 'code',
|
||||||
|
scope: this.scope.join(' '),
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `https://discord.com/api/oauth2/authorize?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
return { url };
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyCredentials() {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
client_id: this.connectionData.consumerKey as string,
|
||||||
|
redirect_uri: this.oauthRedirectUrl,
|
||||||
|
response_type: 'code',
|
||||||
|
scope: this.scope.join(' '),
|
||||||
|
client_secret: this.connectionData.consumerSecret as string,
|
||||||
|
code: this.connectionData.oauthVerifier as string,
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
});
|
||||||
|
const { data: verifiedCredentials } = await this.client.post(
|
||||||
|
'/oauth2/token',
|
||||||
|
params.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
access_token: accessToken,
|
||||||
|
refresh_token: refreshToken,
|
||||||
|
expires_in: expiresIn,
|
||||||
|
scope: scope,
|
||||||
|
token_type: tokenType,
|
||||||
|
} = verifiedCredentials;
|
||||||
|
|
||||||
|
const { data: user } = await this.client.get('/users/@me', {
|
||||||
|
headers: {
|
||||||
|
Authorization: `${tokenType} ${accessToken}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
consumerKey: this.connectionData.consumerKey,
|
||||||
|
consumerSecret: this.connectionData.consumerSecret,
|
||||||
|
accessToken,
|
||||||
|
refreshToken,
|
||||||
|
expiresIn,
|
||||||
|
scope,
|
||||||
|
tokenType,
|
||||||
|
userId: user.id,
|
||||||
|
screenName: user.username,
|
||||||
|
email: user.email,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async isStillVerified() {
|
||||||
|
try {
|
||||||
|
await this.client.get('/users/@me', {
|
||||||
|
headers: {
|
||||||
|
Authorization: `${this.connectionData.tokenType} ${this.connectionData.accessToken}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,12 +0,0 @@
|
|||||||
import { TBeforeRequest } from '@automatisch/types';
|
|
||||||
|
|
||||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
|
||||||
const { tokenType, botToken } = $.auth.data;
|
|
||||||
if (tokenType && botToken) {
|
|
||||||
requestConfig.headers.Authorization = `Bot ${botToken}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -1,10 +0,0 @@
|
|||||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
|
||||||
|
|
||||||
const getCurrentUser = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
|
||||||
const response = await $.http.get('/users/@me');
|
|
||||||
const currentUser = response.data;
|
|
||||||
|
|
||||||
return currentUser;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getCurrentUser;
|
|
@@ -1,3 +0,0 @@
|
|||||||
const scopes = ['bot', 'identify'];
|
|
||||||
|
|
||||||
export default scopes;
|
|
@@ -1,3 +0,0 @@
|
|||||||
import listChannels from './list-channels';
|
|
||||||
|
|
||||||
export default [listChannels];
|
|
@@ -1,34 +0,0 @@
|
|||||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'List channels',
|
|
||||||
key: 'listChannels',
|
|
||||||
|
|
||||||
async run($: IGlobalVariable) {
|
|
||||||
const channels: {
|
|
||||||
data: IJSONObject[];
|
|
||||||
error: IJSONObject | null;
|
|
||||||
} = {
|
|
||||||
data: [],
|
|
||||||
error: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await $.http.get(
|
|
||||||
`/guilds/${$.auth.data.guildId}/channels`
|
|
||||||
);
|
|
||||||
|
|
||||||
channels.data = response.data
|
|
||||||
.filter((channel: IJSONObject) => {
|
|
||||||
// filter in text channels only
|
|
||||||
return channel.type === 0;
|
|
||||||
})
|
|
||||||
.map((channel: IJSONObject) => {
|
|
||||||
return {
|
|
||||||
value: channel.id,
|
|
||||||
name: channel.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return channels;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,22 +1,15 @@
|
|||||||
import defineApp from '../../helpers/define-app';
|
import Authentication from './authentication';
|
||||||
import addAuthHeader from './common/add-auth-header';
|
import {
|
||||||
import auth from './auth';
|
IService,
|
||||||
import dynamicData from './dynamic-data';
|
IAuthentication,
|
||||||
import actions from './actions';
|
IApp,
|
||||||
import triggers from './triggers';
|
IJSONObject,
|
||||||
|
} from '@automatisch/types';
|
||||||
|
|
||||||
export default defineApp({
|
export default class Discord implements IService {
|
||||||
name: 'Discord',
|
authenticationClient: IAuthentication;
|
||||||
key: 'discord',
|
|
||||||
iconUrl: '{BASE_URL}/apps/discord/assets/favicon.svg',
|
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||||
authDocUrl: 'https://automatisch.io/docs/apps/discord/connection',
|
this.authenticationClient = new Authentication(appData, connectionData);
|
||||||
supportsConnections: true,
|
}
|
||||||
baseUrl: 'https://discord.com',
|
}
|
||||||
apiBaseUrl: 'https://discord.com/api',
|
|
||||||
primaryColor: '5865f2',
|
|
||||||
beforeRequest: [addAuthHeader],
|
|
||||||
auth,
|
|
||||||
dynamicData,
|
|
||||||
triggers,
|
|
||||||
actions,
|
|
||||||
});
|
|
||||||
|
219
packages/backend/src/apps/discord/info.json
Normal file
219
packages/backend/src/apps/discord/info.json
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
{
|
||||||
|
"name": "Discord",
|
||||||
|
"key": "discord",
|
||||||
|
"iconUrl": "{BASE_URL}/apps/discord/assets/favicon.svg",
|
||||||
|
"docUrl": "https://automatisch.io/docs/discord",
|
||||||
|
"primaryColor": "5865f2",
|
||||||
|
"supportsConnections": true,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"key": "oAuthRedirectUrl",
|
||||||
|
"label": "OAuth Redirect URL",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": true,
|
||||||
|
"value": "{WEB_APP_URL}/app/discord/connections/add",
|
||||||
|
"placeholder": null,
|
||||||
|
"description": "When asked to input an OAuth callback or redirect URL in Discord OAuth, enter the URL above.",
|
||||||
|
"docUrl": "https://automatisch.io/docs/discord#oauth-redirect-url",
|
||||||
|
"clickToCopy": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "consumerKey",
|
||||||
|
"label": "Consumer Key",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": false,
|
||||||
|
"value": null,
|
||||||
|
"placeholder": null,
|
||||||
|
"description": null,
|
||||||
|
"docUrl": "https://automatisch.io/docs/discord#consumer-key",
|
||||||
|
"clickToCopy": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "consumerSecret",
|
||||||
|
"label": "Consumer Secret",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": false,
|
||||||
|
"value": null,
|
||||||
|
"placeholder": null,
|
||||||
|
"description": null,
|
||||||
|
"docUrl": "https://automatisch.io/docs/discord#consumer-secret",
|
||||||
|
"clickToCopy": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"authenticationSteps": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "{key}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "consumerKey",
|
||||||
|
"value": "{fields.consumerKey}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "consumerSecret",
|
||||||
|
"value": "{fields.consumerSecret}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createAuthData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"type": "openWithPopup",
|
||||||
|
"name": "openAuthPopup",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"value": "{createAuthData.url}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "oauthVerifier",
|
||||||
|
"value": "{openAuthPopup.code}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "verifyConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reconnectionSteps": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "resetConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "consumerKey",
|
||||||
|
"value": "{fields.consumerKey}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "consumerSecret",
|
||||||
|
"value": "{fields.consumerSecret}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createAuthData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"type": "openWithPopup",
|
||||||
|
"name": "openAuthPopup",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"value": "{createAuthData.url}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "oauthVerifier",
|
||||||
|
"value": "{openAuthPopup.code}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 6,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "verifyConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -1 +0,0 @@
|
|||||||
export default [];
|
|
35
packages/backend/src/apps/firebase/assets/favicon.svg
Normal file
35
packages/backend/src/apps/firebase/assets/favicon.svg
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg width="256px" height="351px" viewBox="0 0 256 351" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||||
|
<defs>
|
||||||
|
<path d="M1.25273437,280.731641 L2.85834533,277.600858 L102.211177,89.0833546 L58.0613266,5.6082033 C54.3920011,-1.28304578 45.0741245,0.473674398 43.8699203,8.18789086 L1.25273437,280.731641 Z" id="path-1"></path>
|
||||||
|
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-2">
|
||||||
|
<feGaussianBlur stdDeviation="17.5" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
||||||
|
<feOffset dx="0" dy="0" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
||||||
|
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
||||||
|
</filter>
|
||||||
|
<path d="M134.417103,148.974235 L166.455722,116.161738 L134.417104,55.1546874 C131.374828,49.3635911 123.983911,48.7568362 120.973828,54.5646483 L103.26875,88.6738296 L102.739423,90.4175473 L134.417103,148.974235 Z" id="path-3"></path>
|
||||||
|
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-4">
|
||||||
|
<feGaussianBlur stdDeviation="3.5" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
||||||
|
<feOffset dx="1" dy="-9" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
||||||
|
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.09 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<path d="M0,282.99762 L2.12250746,280.0256 L102.527363,89.5119284 L102.739423,87.4951323 L58.478806,4.35817711 C54.7706269,-2.60604179 44.3313035,-0.845245771 43.1143483,6.95065473 L0,282.99762 Z" fill="#FFC24A"></path>
|
||||||
|
<g>
|
||||||
|
<use fill="#FFA712" fill-rule="evenodd" xlink:href="#path-1"></use>
|
||||||
|
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
|
||||||
|
</g>
|
||||||
|
<path d="M135.004975,150.380704 L167.960199,116.629461 L134.995423,53.6993114 C131.866109,47.7425353 123.128817,47.7253411 120.032618,53.6993112 L102.421015,87.2880848 L102.421015,90.1487443 L135.004975,150.380704 Z" fill="#F4BD62"></path>
|
||||||
|
<g>
|
||||||
|
<use fill="#FFA50E" fill-rule="evenodd" xlink:href="#path-3"></use>
|
||||||
|
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-3"></use>
|
||||||
|
</g>
|
||||||
|
<polygon fill="#F6820C" points="0 282.99762 0.962097168 282.030396 4.45771144 280.60956 132.935323 152.60956 134.563025 148.178595 102.513123 87.1048584"></polygon>
|
||||||
|
<path d="M139.120971,347.551268 L255.395916,282.703666 L222.191698,78.2093373 C221.153051,71.8112478 213.303658,69.2818149 208.724314,73.8694368 L0.000254726368,282.997875 L115.608454,347.545536 C122.914643,351.624979 131.812872,351.62689 139.120971,347.551268" fill="#FDE068"></path>
|
||||||
|
<path d="M254.354084,282.159837 L221.401937,79.2179369 C220.371175,72.8684188 213.843792,70.2409553 209.299213,74.79375 L1.28945312,282.600785 L115.627825,346.509458 C122.878548,350.557931 131.709226,350.559827 138.961846,346.515146 L254.354084,282.159837 Z" fill="#FCCA3F"></path>
|
||||||
|
<path d="M139.120907,345.64082 C131.812808,349.716442 122.914579,349.714531 115.60839,345.635089 L0.93134768,282.014551 L0.000191044776,282.997875 L115.60839,347.545536 C122.914579,351.624979 131.812808,351.62689 139.120907,347.551268 L255.395853,282.703666 L255.111196,280.951785 L139.120907,345.64082 Z" fill="#EEAB37"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
89
packages/backend/src/apps/firebase/authentication.ts
Normal file
89
packages/backend/src/apps/firebase/authentication.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import type {
|
||||||
|
IAuthentication,
|
||||||
|
IApp,
|
||||||
|
IField,
|
||||||
|
IJSONObject,
|
||||||
|
} from '@automatisch/types';
|
||||||
|
import { google as GoogleApi } from 'googleapis';
|
||||||
|
import { OAuth2Client } from 'google-auth-library';
|
||||||
|
|
||||||
|
export default class Authentication implements IAuthentication {
|
||||||
|
appData: IApp;
|
||||||
|
connectionData: IJSONObject;
|
||||||
|
client: OAuth2Client;
|
||||||
|
|
||||||
|
scopes: string[] = [
|
||||||
|
'https://www.googleapis.com/auth/datastore',
|
||||||
|
'https://www.googleapis.com/auth/firebase',
|
||||||
|
'https://www.googleapis.com/auth/user.emails.read',
|
||||||
|
'profile',
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||||
|
this.appData = appData;
|
||||||
|
this.connectionData = connectionData;
|
||||||
|
|
||||||
|
this.client = new GoogleApi.auth.OAuth2(
|
||||||
|
connectionData.consumerKey as string,
|
||||||
|
connectionData.consumerSecret as string,
|
||||||
|
this.oauthRedirectUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
GoogleApi.options({ auth: this.client });
|
||||||
|
}
|
||||||
|
|
||||||
|
get oauthRedirectUrl() {
|
||||||
|
return this.appData.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAuthData() {
|
||||||
|
const url = this.client.generateAuthUrl({
|
||||||
|
access_type: 'offline',
|
||||||
|
scope: this.scopes,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { url };
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyCredentials() {
|
||||||
|
const { tokens } = await this.client.getToken(
|
||||||
|
this.connectionData.oauthVerifier as string
|
||||||
|
);
|
||||||
|
this.client.setCredentials(tokens);
|
||||||
|
|
||||||
|
const people = GoogleApi.people('v1');
|
||||||
|
|
||||||
|
const { data } = await people.people.get({
|
||||||
|
resourceName: 'people/me',
|
||||||
|
personFields: 'emailAddresses',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { emailAddresses, resourceName: userId } = data;
|
||||||
|
const primaryEmailAddress = emailAddresses.find(
|
||||||
|
(emailAddress) => emailAddress.metadata.primary
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
consumerKey: this.connectionData.consumerKey,
|
||||||
|
consumerSecret: this.connectionData.consumerSecret,
|
||||||
|
accessToken: tokens.access_token,
|
||||||
|
refreshToken: tokens.refresh_token,
|
||||||
|
tokenType: tokens.token_type,
|
||||||
|
expiryDate: tokens.expiry_date,
|
||||||
|
scope: tokens.scope,
|
||||||
|
screenName: primaryEmailAddress.value,
|
||||||
|
userId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async isStillVerified() {
|
||||||
|
try {
|
||||||
|
await this.client.getTokenInfo(this.connectionData.accessToken as string);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
packages/backend/src/apps/firebase/index.ts
Normal file
15
packages/backend/src/apps/firebase/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Authentication from './authentication';
|
||||||
|
import {
|
||||||
|
IService,
|
||||||
|
IAuthentication,
|
||||||
|
IApp,
|
||||||
|
IJSONObject,
|
||||||
|
} from '@automatisch/types';
|
||||||
|
|
||||||
|
export default class Firebase implements IService {
|
||||||
|
authenticationClient: IAuthentication;
|
||||||
|
|
||||||
|
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||||
|
this.authenticationClient = new Authentication(appData, connectionData);
|
||||||
|
}
|
||||||
|
}
|
219
packages/backend/src/apps/firebase/info.json
Normal file
219
packages/backend/src/apps/firebase/info.json
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
{
|
||||||
|
"name": "Firebase",
|
||||||
|
"key": "firebase",
|
||||||
|
"iconUrl": "{BASE_URL}/apps/firebase/assets/favicon.svg",
|
||||||
|
"docUrl": "https://automatisch.io/docs/firebase",
|
||||||
|
"primaryColor": "ffca28",
|
||||||
|
"supportsConnections": true,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"key": "oAuthRedirectUrl",
|
||||||
|
"label": "OAuth Redirect URL",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": true,
|
||||||
|
"value": "{WEB_APP_URL}/app/firebase/connections/add",
|
||||||
|
"placeholder": null,
|
||||||
|
"description": "When asked to input an OAuth callback or redirect URL in Firebase OAuth, enter the URL above.",
|
||||||
|
"docUrl": "https://automatisch.io/docs/firebase#oauth-redirect-url",
|
||||||
|
"clickToCopy": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "consumerKey",
|
||||||
|
"label": "Consumer Key",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": false,
|
||||||
|
"value": null,
|
||||||
|
"placeholder": null,
|
||||||
|
"description": null,
|
||||||
|
"docUrl": "https://automatisch.io/docs/firebase#consumer-key",
|
||||||
|
"clickToCopy": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "consumerSecret",
|
||||||
|
"label": "Consumer Secret",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": false,
|
||||||
|
"value": null,
|
||||||
|
"placeholder": null,
|
||||||
|
"description": null,
|
||||||
|
"docUrl": "https://automatisch.io/docs/firebase#consumer-secret",
|
||||||
|
"clickToCopy": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"authenticationSteps": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "{key}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "consumerKey",
|
||||||
|
"value": "{fields.consumerKey}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "consumerSecret",
|
||||||
|
"value": "{fields.consumerSecret}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createAuthData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"type": "openWithPopup",
|
||||||
|
"name": "openAuthPopup",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"value": "{createAuthData.url}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "oauthVerifier",
|
||||||
|
"value": "{openAuthPopup.code}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "verifyConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reconnectionSteps": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "resetConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "consumerKey",
|
||||||
|
"value": "{fields.consumerKey}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "consumerSecret",
|
||||||
|
"value": "{fields.consumerSecret}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createAuthData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"type": "openWithPopup",
|
||||||
|
"name": "openAuthPopup",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"value": "{createAuthData.url}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "oauthVerifier",
|
||||||
|
"value": "{openAuthPopup.code}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 6,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "verifyConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -1,21 +0,0 @@
|
|||||||
import { IField, IGlobalVariable } from '@automatisch/types';
|
|
||||||
import { URLSearchParams } from 'url';
|
|
||||||
|
|
||||||
export default async function generateAuthUrl($: IGlobalVariable) {
|
|
||||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
|
||||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
|
||||||
);
|
|
||||||
|
|
||||||
const callbackUrl = oauthRedirectUrlField.value;
|
|
||||||
const requestPath = '/oauth/request_token';
|
|
||||||
const data = { oauth_callback: callbackUrl };
|
|
||||||
|
|
||||||
const response = await $.http.post(requestPath, data);
|
|
||||||
const responseData = Object.fromEntries(new URLSearchParams(response.data));
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
url: `${$.app.apiBaseUrl}/oauth/authorize?oauth_token=${responseData.oauth_token}&perms=delete`,
|
|
||||||
accessToken: responseData.oauth_token,
|
|
||||||
accessSecret: responseData.oauth_token_secret,
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,48 +0,0 @@
|
|||||||
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/flickr/connections/add',
|
|
||||||
placeholder: null,
|
|
||||||
description:
|
|
||||||
'When asked to input an OAuth callback or redirect URL in Flickr OAuth, enter the URL above.',
|
|
||||||
docUrl: 'https://automatisch.io/docs/flickr#oauth-redirect-url',
|
|
||||||
clickToCopy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'consumerKey',
|
|
||||||
label: 'Consumer Key',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
docUrl: 'https://automatisch.io/docs/flickr#consumer-key',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'consumerSecret',
|
|
||||||
label: 'Consumer Secret',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
docUrl: 'https://automatisch.io/docs/flickr#consumer-secret',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
generateAuthUrl,
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
};
|
|
@@ -1,13 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
|
|
||||||
const isStillVerified = async ($: IGlobalVariable) => {
|
|
||||||
const params = {
|
|
||||||
method: 'flickr.test.login',
|
|
||||||
format: 'json',
|
|
||||||
nojsoncallback: 1,
|
|
||||||
};
|
|
||||||
const response = await $.http.get('/rest', { params });
|
|
||||||
return !!response.data.user.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default isStillVerified;
|
|
@@ -1,22 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
import { URLSearchParams } from 'url';
|
|
||||||
|
|
||||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
|
||||||
const response = await $.http.post(
|
|
||||||
`/oauth/access_token?oauth_verifier=${$.auth.data.oauth_verifier}&oauth_token=${$.auth.data.accessToken}`,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
const responseData = Object.fromEntries(new URLSearchParams(response.data));
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
consumerKey: $.auth.data.consumerKey,
|
|
||||||
consumerSecret: $.auth.data.consumerSecret,
|
|
||||||
accessToken: responseData.oauth_token,
|
|
||||||
accessSecret: responseData.oauth_token_secret,
|
|
||||||
userId: responseData.user_nsid,
|
|
||||||
screenName: responseData.fullname,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
82
packages/backend/src/apps/flickr/authentication.ts
Normal file
82
packages/backend/src/apps/flickr/authentication.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import type {
|
||||||
|
IAuthentication,
|
||||||
|
IApp,
|
||||||
|
IField,
|
||||||
|
IJSONObject,
|
||||||
|
} from '@automatisch/types';
|
||||||
|
import FlickrApi from 'flickr-sdk';
|
||||||
|
|
||||||
|
export default class Authentication implements IAuthentication {
|
||||||
|
appData: IApp;
|
||||||
|
connectionData: IJSONObject;
|
||||||
|
client: typeof FlickrApi;
|
||||||
|
oauthClient: typeof FlickrApi;
|
||||||
|
|
||||||
|
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||||
|
this.oauthClient = new FlickrApi.OAuth(
|
||||||
|
connectionData.consumerKey,
|
||||||
|
connectionData.consumerSecret
|
||||||
|
);
|
||||||
|
|
||||||
|
if (connectionData.accessToken && connectionData.accessSecret) {
|
||||||
|
this.client = new FlickrApi(
|
||||||
|
FlickrApi.OAuth.createPlugin(
|
||||||
|
connectionData.consumerKey,
|
||||||
|
connectionData.consumerSecret,
|
||||||
|
connectionData.accessToken,
|
||||||
|
connectionData.accessSecret
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connectionData = connectionData;
|
||||||
|
this.appData = appData;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAuthData() {
|
||||||
|
const appFields = this.appData.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const callbackUrl = appFields.value;
|
||||||
|
|
||||||
|
const oauthData = (await this.oauthClient.request(callbackUrl)).body;
|
||||||
|
const url = await this.oauthClient.authorizeUrl(
|
||||||
|
oauthData.oauth_token,
|
||||||
|
'delete'
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: oauthData.oauth_token,
|
||||||
|
accessSecret: oauthData.oauth_token_secret,
|
||||||
|
url: url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyCredentials() {
|
||||||
|
const verifiedCredentials = (
|
||||||
|
await this.oauthClient.verify(
|
||||||
|
this.connectionData.accessToken,
|
||||||
|
this.connectionData.oauthVerifier,
|
||||||
|
this.connectionData.accessSecret
|
||||||
|
)
|
||||||
|
).body;
|
||||||
|
|
||||||
|
return {
|
||||||
|
consumerKey: this.connectionData.consumerKey,
|
||||||
|
consumerSecret: this.connectionData.consumerSecret,
|
||||||
|
accessToken: verifiedCredentials.oauth_token,
|
||||||
|
accessSecret: verifiedCredentials.oauth_token_secret,
|
||||||
|
userId: verifiedCredentials.user_nsid,
|
||||||
|
screenName: verifiedCredentials.fullname,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async isStillVerified() {
|
||||||
|
try {
|
||||||
|
await this.client.test.login();
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,41 +0,0 @@
|
|||||||
import { Token } from 'oauth-1.0a';
|
|
||||||
import { IJSONObject, TBeforeRequest } from '@automatisch/types';
|
|
||||||
import oauthClient from './oauth-client';
|
|
||||||
|
|
||||||
type RequestDataType = {
|
|
||||||
url: string;
|
|
||||||
method: string;
|
|
||||||
data?: IJSONObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
|
||||||
const { url, method, data, params } = requestConfig;
|
|
||||||
|
|
||||||
const token: Token = {
|
|
||||||
key: $.auth.data?.accessToken as string,
|
|
||||||
secret: $.auth.data?.accessSecret as string,
|
|
||||||
};
|
|
||||||
|
|
||||||
const requestData: RequestDataType = {
|
|
||||||
url: `${requestConfig.baseURL}${url}`,
|
|
||||||
method,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (url === '/oauth/request_token') {
|
|
||||||
requestData.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method === 'get') {
|
|
||||||
requestData.data = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
const authHeader = oauthClient($).toHeader(
|
|
||||||
oauthClient($).authorize(requestData, token)
|
|
||||||
);
|
|
||||||
|
|
||||||
requestConfig.headers.Authorization = authHeader.Authorization;
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -1,23 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
import crypto from 'crypto';
|
|
||||||
import OAuth from 'oauth-1.0a';
|
|
||||||
|
|
||||||
const oauthClient = ($: IGlobalVariable) => {
|
|
||||||
const consumerData = {
|
|
||||||
key: $.auth.data.consumerKey as string,
|
|
||||||
secret: $.auth.data.consumerSecret as string,
|
|
||||||
};
|
|
||||||
|
|
||||||
return new OAuth({
|
|
||||||
consumer: consumerData,
|
|
||||||
signature_method: 'HMAC-SHA1',
|
|
||||||
hash_function(base_string, key) {
|
|
||||||
return crypto
|
|
||||||
.createHmac('sha1', key)
|
|
||||||
.update(base_string)
|
|
||||||
.digest('base64');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default oauthClient;
|
|
10
packages/backend/src/apps/flickr/data.ts
Normal file
10
packages/backend/src/apps/flickr/data.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
import ListAlbums from './data/list-albums';
|
||||||
|
|
||||||
|
export default class Data {
|
||||||
|
listAlbums: ListAlbums;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject) {
|
||||||
|
this.listAlbums = new ListAlbums(connectionData);
|
||||||
|
}
|
||||||
|
}
|
39
packages/backend/src/apps/flickr/data/list-albums.ts
Normal file
39
packages/backend/src/apps/flickr/data/list-albums.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import FlickrApi from 'flickr-sdk';
|
||||||
|
import type { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
export default class ListAlbums {
|
||||||
|
client?: typeof FlickrApi;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject) {
|
||||||
|
if (
|
||||||
|
connectionData.consumerKey &&
|
||||||
|
connectionData.consumerSecret &&
|
||||||
|
connectionData.accessToken &&
|
||||||
|
connectionData.accessSecret
|
||||||
|
) {
|
||||||
|
this.client = new FlickrApi(
|
||||||
|
FlickrApi.OAuth.createPlugin(
|
||||||
|
connectionData.consumerKey,
|
||||||
|
connectionData.consumerSecret,
|
||||||
|
connectionData.accessToken,
|
||||||
|
connectionData.accessSecret
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
const { photosets } = (await this.client.photosets.getList()).body;
|
||||||
|
const allPhotosets = [...photosets.photoset];
|
||||||
|
|
||||||
|
for (let page = photosets.page + 1; page <= photosets.pages; page++) {
|
||||||
|
const { photosets } = (await this.client.photosets.getList({ page, })).body;
|
||||||
|
allPhotosets.push(...photosets.photoset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allPhotosets.map((photoset) => ({
|
||||||
|
value: photoset.id,
|
||||||
|
name: photoset.title._content,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +0,0 @@
|
|||||||
import listAlbums from './list-albums';
|
|
||||||
|
|
||||||
export default [listAlbums];
|
|
@@ -1,56 +0,0 @@
|
|||||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
|
||||||
|
|
||||||
type TResponse = {
|
|
||||||
data: IJSONObject[];
|
|
||||||
error?: IJSONObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TPhotoset = {
|
|
||||||
id: string;
|
|
||||||
title: {
|
|
||||||
_content: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'List albums',
|
|
||||||
key: 'listAlbums',
|
|
||||||
|
|
||||||
async run($: IGlobalVariable) {
|
|
||||||
const params = {
|
|
||||||
page: 1,
|
|
||||||
per_page: 500,
|
|
||||||
user_id: $.auth.data.userId,
|
|
||||||
method: 'flickr.photosets.getList',
|
|
||||||
format: 'json',
|
|
||||||
nojsoncallback: 1,
|
|
||||||
};
|
|
||||||
let response = await $.http.get('/rest', { params });
|
|
||||||
|
|
||||||
const aggregatedResponse: TResponse = {
|
|
||||||
data: [...response.data.photosets.photoset],
|
|
||||||
};
|
|
||||||
|
|
||||||
while (response.data.photosets.page < response.data.photosets.pages) {
|
|
||||||
response = await $.http.get('/rest', {
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
page: response.data.photosets.page,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
aggregatedResponse.data.push(...response.data.photosets.photoset);
|
|
||||||
}
|
|
||||||
|
|
||||||
aggregatedResponse.data = aggregatedResponse.data.map(
|
|
||||||
(photoset: TPhotoset) => {
|
|
||||||
return {
|
|
||||||
value: photoset.id,
|
|
||||||
name: photoset.title._content,
|
|
||||||
} as IJSONObject;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return aggregatedResponse;
|
|
||||||
},
|
|
||||||
};
|
|
1
packages/backend/src/apps/flickr/index.d.ts
vendored
1
packages/backend/src/apps/flickr/index.d.ts
vendored
@@ -0,0 +1 @@
|
|||||||
|
declare module 'flickr-sdk';
|
||||||
|
@@ -1,21 +1,25 @@
|
|||||||
import defineApp from '../../helpers/define-app';
|
import {
|
||||||
import addAuthHeader from './common/add-auth-header';
|
IService,
|
||||||
import auth from './auth';
|
IAuthentication,
|
||||||
import triggers from './triggers';
|
IApp,
|
||||||
import dynamicData from './dynamic-data';
|
IJSONObject,
|
||||||
|
} from '@automatisch/types';
|
||||||
|
import Authentication from './authentication';
|
||||||
|
import Triggers from './triggers';
|
||||||
|
import Data from './data';
|
||||||
|
|
||||||
export default defineApp({
|
export default class Flickr implements IService {
|
||||||
name: 'Flickr',
|
authenticationClient: IAuthentication;
|
||||||
key: 'flickr',
|
triggers: Triggers;
|
||||||
iconUrl: '{BASE_URL}/apps/flickr/assets/favicon.svg',
|
data: Data;
|
||||||
authDocUrl: 'https://automatisch.io/docs/apps/flickr/connection',
|
|
||||||
docUrl: 'https://automatisch.io/docs/flickr',
|
constructor(
|
||||||
primaryColor: '000000',
|
appData: IApp,
|
||||||
supportsConnections: true,
|
connectionData: IJSONObject,
|
||||||
baseUrl: 'https://www.flickr.com/',
|
parameters: IJSONObject
|
||||||
apiBaseUrl: 'https://www.flickr.com/services',
|
) {
|
||||||
beforeRequest: [addAuthHeader],
|
this.authenticationClient = new Authentication(appData, connectionData);
|
||||||
auth,
|
this.data = new Data(connectionData);
|
||||||
triggers,
|
this.triggers = new Triggers(connectionData, parameters);
|
||||||
dynamicData,
|
}
|
||||||
});
|
}
|
||||||
|
304
packages/backend/src/apps/flickr/info.json
Normal file
304
packages/backend/src/apps/flickr/info.json
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
{
|
||||||
|
"name": "Flickr",
|
||||||
|
"key": "flickr",
|
||||||
|
"iconUrl": "{BASE_URL}/apps/flickr/assets/favicon.svg",
|
||||||
|
"docUrl": "https://automatisch.io/docs/flickr",
|
||||||
|
"primaryColor": "000000",
|
||||||
|
"supportsConnections": true,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"key": "oAuthRedirectUrl",
|
||||||
|
"label": "OAuth Redirect URL",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": true,
|
||||||
|
"value": "{WEB_APP_URL}/app/flickr/connections/add",
|
||||||
|
"placeholder": null,
|
||||||
|
"description": "When asked to input an OAuth callback or redirect URL in Flickr OAuth, enter the URL above.",
|
||||||
|
"docUrl": "https://automatisch.io/docs/flickr#oauth-redirect-url",
|
||||||
|
"clickToCopy": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "consumerKey",
|
||||||
|
"label": "Consumer Key",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": false,
|
||||||
|
"value": null,
|
||||||
|
"placeholder": null,
|
||||||
|
"description": null,
|
||||||
|
"docUrl": "https://automatisch.io/docs/flickr#consumer-key",
|
||||||
|
"clickToCopy": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "consumerSecret",
|
||||||
|
"label": "Consumer Secret",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": false,
|
||||||
|
"value": null,
|
||||||
|
"placeholder": null,
|
||||||
|
"description": null,
|
||||||
|
"docUrl": "https://automatisch.io/docs/flickr#consumer-secret",
|
||||||
|
"clickToCopy": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"authenticationSteps": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "{key}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "consumerKey",
|
||||||
|
"value": "{fields.consumerKey}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "consumerSecret",
|
||||||
|
"value": "{fields.consumerSecret}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createAuthData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"type": "openWithPopup",
|
||||||
|
"name": "openAuthPopup",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"value": "{createAuthData.url}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "oauthVerifier",
|
||||||
|
"value": "{openAuthPopup.oauth_verifier}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "verifyConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reconnectionSteps": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "resetConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "consumerKey",
|
||||||
|
"value": "{fields.consumerKey}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "consumerSecret",
|
||||||
|
"value": "{fields.consumerSecret}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createAuthData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"type": "openWithPopup",
|
||||||
|
"name": "openAuthPopup",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"value": "{createAuthData.url}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "oauthVerifier",
|
||||||
|
"value": "{openAuthPopup.oauth_verifier}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 6,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "verifyConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"triggers": [
|
||||||
|
{
|
||||||
|
"name": "New favorite photo",
|
||||||
|
"key": "newFavoritePhoto",
|
||||||
|
"description": "Triggers when you favorite a photo.",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New photo in album",
|
||||||
|
"key": "newPhotoInAlbum",
|
||||||
|
"description": "Triggers when you add a new photo in an album.",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Album",
|
||||||
|
"key": "album",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listAlbums"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New photo",
|
||||||
|
"key": "newPhoto",
|
||||||
|
"description": "Triggers when you add a new photo.",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New album",
|
||||||
|
"key": "newAlbum",
|
||||||
|
"description": "Triggers when you create a new album.",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
19
packages/backend/src/apps/flickr/triggers.ts
Normal file
19
packages/backend/src/apps/flickr/triggers.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
import NewFavoritePhoto from './triggers/new-favorite-photo';
|
||||||
|
import NewPhotoInAlbum from './triggers/new-photo-in-album';
|
||||||
|
import NewPhoto from './triggers/new-photo';
|
||||||
|
import NewAlbum from './triggers/new-album';
|
||||||
|
|
||||||
|
export default class Triggers {
|
||||||
|
newFavoritePhoto: NewFavoritePhoto;
|
||||||
|
newPhotoInAlbum: NewPhotoInAlbum;
|
||||||
|
newPhoto: NewPhoto;
|
||||||
|
newAlbum: NewAlbum;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
this.newFavoritePhoto = new NewFavoritePhoto(connectionData);
|
||||||
|
this.newPhotoInAlbum = new NewPhotoInAlbum(connectionData, parameters);
|
||||||
|
this.newPhoto = new NewPhoto(connectionData);
|
||||||
|
this.newAlbum = new NewAlbum(connectionData);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +0,0 @@
|
|||||||
import newAlbums from './new-albums';
|
|
||||||
import newFavoritePhotos from './new-favorite-photos';
|
|
||||||
import newPhotos from './new-photos';
|
|
||||||
import newPhotosInAlbums from './new-photos-in-album';
|
|
||||||
|
|
||||||
export default [newAlbums, newFavoritePhotos, newPhotos, newPhotosInAlbums];
|
|
103
packages/backend/src/apps/flickr/triggers/new-album.ts
Normal file
103
packages/backend/src/apps/flickr/triggers/new-album.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import FlickrApi from 'flickr-sdk';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
export default class NewAlbum {
|
||||||
|
client?: typeof FlickrApi;
|
||||||
|
connectionData?: IJSONObject;
|
||||||
|
primaryPhotoExtraFields = [
|
||||||
|
'description',
|
||||||
|
'license',
|
||||||
|
'date_upload',
|
||||||
|
'date_taken',
|
||||||
|
'owner_name',
|
||||||
|
'icon_server',
|
||||||
|
'original_format',
|
||||||
|
'last_update',
|
||||||
|
'geo',
|
||||||
|
'tags',
|
||||||
|
'machine_tags',
|
||||||
|
'o_dims',
|
||||||
|
'views',
|
||||||
|
'media',
|
||||||
|
'path_alias',
|
||||||
|
'url_sq',
|
||||||
|
'url_t',
|
||||||
|
'url_s',
|
||||||
|
'url_q',
|
||||||
|
'url_m',
|
||||||
|
'url_n',
|
||||||
|
'url_z',
|
||||||
|
'url_c',
|
||||||
|
'url_l',
|
||||||
|
'url_o',
|
||||||
|
].join(',');
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject) {
|
||||||
|
if (
|
||||||
|
connectionData.consumerKey &&
|
||||||
|
connectionData.consumerSecret &&
|
||||||
|
connectionData.accessToken &&
|
||||||
|
connectionData.accessSecret
|
||||||
|
) {
|
||||||
|
this.client = new FlickrApi(
|
||||||
|
FlickrApi.OAuth.createPlugin(
|
||||||
|
connectionData.consumerKey,
|
||||||
|
connectionData.consumerSecret,
|
||||||
|
connectionData.accessToken,
|
||||||
|
connectionData.accessSecret
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.connectionData = connectionData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAlbums(options: { perPage?: number, page?: number } = {}) {
|
||||||
|
const { perPage, page } = options;
|
||||||
|
const payload = {
|
||||||
|
page,
|
||||||
|
per_page: perPage,
|
||||||
|
primary_photo_extras: this.primaryPhotoExtraFields,
|
||||||
|
};
|
||||||
|
const { photosets } = (await this.client.photosets.getList(payload)).body;
|
||||||
|
|
||||||
|
return photosets;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(startTime: Date) {
|
||||||
|
const albums = await this.getAlbums({ page: 1 });
|
||||||
|
const allAlbums = [...albums.photoset];
|
||||||
|
const newAlbums = [];
|
||||||
|
|
||||||
|
let page = 1;
|
||||||
|
for (const album of allAlbums) {
|
||||||
|
const createdAtInSeconds = DateTime.fromSeconds(parseInt(album.date_create, 10));
|
||||||
|
const createdAt = createdAtInSeconds.toMillis();
|
||||||
|
|
||||||
|
if (createdAt <= startTime.getTime()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
newAlbums.push(album);
|
||||||
|
|
||||||
|
const currentIndex = allAlbums.indexOf(album);
|
||||||
|
const totalAlbums = allAlbums.length;
|
||||||
|
const isLastItem = currentIndex + 1 === totalAlbums;
|
||||||
|
|
||||||
|
if (isLastItem && page < albums.pages) {
|
||||||
|
page = page + 1;
|
||||||
|
const { photoset } = await this.getAlbums({ page, });
|
||||||
|
allAlbums.push(...photoset.photoset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newAlbums;
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const { photoset } = await this.getAlbums({ perPage: 1 });
|
||||||
|
|
||||||
|
return photoset;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +0,0 @@
|
|||||||
import defineTrigger from '../../../../helpers/define-trigger';
|
|
||||||
import newAlbums from './new-albums';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New albums',
|
|
||||||
pollInterval: 15,
|
|
||||||
key: 'newAlbums',
|
|
||||||
description: 'Triggers when you create a new album.',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
await newAlbums($);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,55 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
|
|
||||||
const extraFields = [
|
|
||||||
'license',
|
|
||||||
'date_upload',
|
|
||||||
'date_taken',
|
|
||||||
'owner_name',
|
|
||||||
'icon_server',
|
|
||||||
'original_format',
|
|
||||||
'last_update',
|
|
||||||
'geo',
|
|
||||||
'tags',
|
|
||||||
'machine_tags',
|
|
||||||
'o_dims',
|
|
||||||
'views',
|
|
||||||
'media',
|
|
||||||
'path_alias',
|
|
||||||
'url_sq',
|
|
||||||
'url_t',
|
|
||||||
'url_s',
|
|
||||||
'url_m',
|
|
||||||
'url_o',
|
|
||||||
].join(',');
|
|
||||||
|
|
||||||
const newAlbums = async ($: IGlobalVariable) => {
|
|
||||||
let page = 1;
|
|
||||||
let pages = 1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
const params = {
|
|
||||||
page,
|
|
||||||
per_page: 500,
|
|
||||||
user_id: $.auth.data.userId,
|
|
||||||
extras: extraFields,
|
|
||||||
method: 'flickr.photosets.getList',
|
|
||||||
format: 'json',
|
|
||||||
nojsoncallback: 1,
|
|
||||||
};
|
|
||||||
const response = await $.http.get('/rest', { params });
|
|
||||||
const photosets = response.data.photosets;
|
|
||||||
page = photosets.page + 1;
|
|
||||||
pages = photosets.pages;
|
|
||||||
|
|
||||||
for (const photoset of photosets.photoset) {
|
|
||||||
$.pushTriggerItem({
|
|
||||||
raw: photoset,
|
|
||||||
meta: {
|
|
||||||
internalId: photoset.id as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} while (page <= pages);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default newAlbums;
|
|
@@ -0,0 +1,62 @@
|
|||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import FlickrApi from 'flickr-sdk';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
export default class NewFavoritePhoto {
|
||||||
|
client?: typeof FlickrApi;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject) {
|
||||||
|
if (
|
||||||
|
connectionData.consumerKey &&
|
||||||
|
connectionData.consumerSecret &&
|
||||||
|
connectionData.accessToken &&
|
||||||
|
connectionData.accessSecret
|
||||||
|
) {
|
||||||
|
this.client = new FlickrApi(
|
||||||
|
FlickrApi.OAuth.createPlugin(
|
||||||
|
connectionData.consumerKey,
|
||||||
|
connectionData.consumerSecret,
|
||||||
|
connectionData.accessToken,
|
||||||
|
connectionData.accessSecret
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(startTime: Date) {
|
||||||
|
const { photos } = (await this.client.favorites.getList()).body;
|
||||||
|
const favPhotos = [...photos.photo];
|
||||||
|
const newFavPhotos = [];
|
||||||
|
|
||||||
|
let page = 1;
|
||||||
|
for (const photo of favPhotos) {
|
||||||
|
const markedFavoriteAt = DateTime.fromSeconds(parseInt(photo.date_faved, 10));
|
||||||
|
const markedFavoriteAtInMillis = markedFavoriteAt.toMillis();
|
||||||
|
|
||||||
|
if (markedFavoriteAtInMillis <= startTime.getTime()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
newFavPhotos.push(photo);
|
||||||
|
|
||||||
|
const currentIndex = favPhotos.indexOf(photo);
|
||||||
|
const totalFavPhotos = favPhotos.length;
|
||||||
|
const isLastItem = currentIndex + 1 === totalFavPhotos;
|
||||||
|
|
||||||
|
if (isLastItem && page < photos.pages) {
|
||||||
|
page = page + 1;
|
||||||
|
const { photos } = (await this.client.favorites.getList({ page, })).body;
|
||||||
|
favPhotos.push(...photos.photo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newFavPhotos;
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const { photos } = (await this.client.favorites.getList({ per_page: 1, })).body;
|
||||||
|
|
||||||
|
return photos.photo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -1,13 +0,0 @@
|
|||||||
import defineTrigger from '../../../../helpers/define-trigger';
|
|
||||||
import newFavoritePhotos from './new-favorite-photos';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New favorite photos',
|
|
||||||
pollInterval: 15,
|
|
||||||
key: 'newFavoritePhotos',
|
|
||||||
description: 'Triggers when you favorite a photo.',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
await newFavoritePhotos($);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,61 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
|
|
||||||
const extraFields = [
|
|
||||||
'description',
|
|
||||||
'license',
|
|
||||||
'date_upload',
|
|
||||||
'date_taken',
|
|
||||||
'owner_name',
|
|
||||||
'icon_server',
|
|
||||||
'original_format',
|
|
||||||
'last_update',
|
|
||||||
'geo',
|
|
||||||
'tags',
|
|
||||||
'machine_tags',
|
|
||||||
'o_dims',
|
|
||||||
'views',
|
|
||||||
'media',
|
|
||||||
'path_alias',
|
|
||||||
'url_sq',
|
|
||||||
'url_t',
|
|
||||||
'url_s',
|
|
||||||
'url_q',
|
|
||||||
'url_m',
|
|
||||||
'url_n',
|
|
||||||
'url_z',
|
|
||||||
'url_c',
|
|
||||||
'url_l',
|
|
||||||
'url_o',
|
|
||||||
].join(',');
|
|
||||||
|
|
||||||
const newPhotos = async ($: IGlobalVariable) => {
|
|
||||||
let page = 1;
|
|
||||||
let pages = 1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
const params = {
|
|
||||||
page,
|
|
||||||
per_page: 500,
|
|
||||||
user_id: $.auth.data.userId,
|
|
||||||
extras: extraFields,
|
|
||||||
method: 'flickr.favorites.getList',
|
|
||||||
format: 'json',
|
|
||||||
nojsoncallback: 1,
|
|
||||||
};
|
|
||||||
const response = await $.http.get('/rest', { params });
|
|
||||||
const photos = response.data.photos;
|
|
||||||
page = photos.page + 1;
|
|
||||||
pages = photos.pages;
|
|
||||||
|
|
||||||
for (const photo of photos.photo) {
|
|
||||||
$.pushTriggerItem({
|
|
||||||
raw: photo,
|
|
||||||
meta: {
|
|
||||||
internalId: photo.date_faved as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} while (page <= pages);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default newPhotos;
|
|
@@ -0,0 +1,80 @@
|
|||||||
|
import FlickrApi from 'flickr-sdk';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
export default class NewPhotoInAlbum {
|
||||||
|
client?: typeof FlickrApi;
|
||||||
|
connectionData?: IJSONObject;
|
||||||
|
albumId?: string;
|
||||||
|
extraFields = [
|
||||||
|
'license',
|
||||||
|
'date_upload',
|
||||||
|
'date_taken',
|
||||||
|
'owner_name',
|
||||||
|
'icon_server',
|
||||||
|
'original_format',
|
||||||
|
'last_update',
|
||||||
|
'geo',
|
||||||
|
'tags',
|
||||||
|
'machine_tags',
|
||||||
|
'o_dims',
|
||||||
|
'views',
|
||||||
|
'media',
|
||||||
|
'path_alias',
|
||||||
|
'url_sq',
|
||||||
|
'url_t',
|
||||||
|
'url_s',
|
||||||
|
'url_m',
|
||||||
|
'url_o'
|
||||||
|
].join(',');
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
if (
|
||||||
|
connectionData.consumerKey &&
|
||||||
|
connectionData.consumerSecret &&
|
||||||
|
connectionData.accessToken &&
|
||||||
|
connectionData.accessSecret
|
||||||
|
) {
|
||||||
|
this.client = new FlickrApi(
|
||||||
|
FlickrApi.OAuth.createPlugin(
|
||||||
|
connectionData.consumerKey,
|
||||||
|
connectionData.consumerSecret,
|
||||||
|
connectionData.accessToken,
|
||||||
|
connectionData.accessSecret
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.connectionData = connectionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters?.album) {
|
||||||
|
this.albumId = parameters.album as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAlbumPhotos(options: { perPage?: number, page?: number } = {}) {
|
||||||
|
const { perPage, page } = options;
|
||||||
|
const payload = {
|
||||||
|
page,
|
||||||
|
per_page: perPage,
|
||||||
|
photoset_id: this.albumId,
|
||||||
|
user_id: this.connectionData.userId,
|
||||||
|
extras: this.extraFields,
|
||||||
|
};
|
||||||
|
const { photoset } = (await this.client.photosets.getPhotos(payload)).body;
|
||||||
|
|
||||||
|
return photoset;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
// TODO: implement pagination on undated entries
|
||||||
|
const { photo } = await this.getAlbumPhotos({ page: 1 });
|
||||||
|
|
||||||
|
return photo;
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const { photo } = await this.getAlbumPhotos({ perPage: 1 });
|
||||||
|
|
||||||
|
return photo;
|
||||||
|
}
|
||||||
|
}
|
88
packages/backend/src/apps/flickr/triggers/new-photo.ts
Normal file
88
packages/backend/src/apps/flickr/triggers/new-photo.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import FlickrApi from 'flickr-sdk';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
export default class NewPhoto {
|
||||||
|
client?: typeof FlickrApi;
|
||||||
|
connectionData?: IJSONObject;
|
||||||
|
extraFields = [
|
||||||
|
'description',
|
||||||
|
'license',
|
||||||
|
'date_upload',
|
||||||
|
'date_taken',
|
||||||
|
'owner_name',
|
||||||
|
'icon_server',
|
||||||
|
'original_format',
|
||||||
|
'last_update',
|
||||||
|
'geo',
|
||||||
|
'tags',
|
||||||
|
'machine_tags',
|
||||||
|
'o_dims',
|
||||||
|
'views',
|
||||||
|
'media',
|
||||||
|
'path_alias',
|
||||||
|
'url_sq',
|
||||||
|
'url_t',
|
||||||
|
'url_s',
|
||||||
|
'url_q',
|
||||||
|
'url_m',
|
||||||
|
'url_n',
|
||||||
|
'url_z',
|
||||||
|
'url_c',
|
||||||
|
'url_l',
|
||||||
|
'url_o',
|
||||||
|
].join(',');
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject) {
|
||||||
|
if (
|
||||||
|
connectionData.consumerKey &&
|
||||||
|
connectionData.consumerSecret &&
|
||||||
|
connectionData.accessToken &&
|
||||||
|
connectionData.accessSecret
|
||||||
|
) {
|
||||||
|
this.client = new FlickrApi(
|
||||||
|
FlickrApi.OAuth.createPlugin(
|
||||||
|
connectionData.consumerKey,
|
||||||
|
connectionData.consumerSecret,
|
||||||
|
connectionData.accessToken,
|
||||||
|
connectionData.accessSecret
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.connectionData = connectionData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPhotos(options: { perPage?: number, page?: number, minUploadDate?: string } = {}) {
|
||||||
|
const { perPage, page, minUploadDate } = options;
|
||||||
|
const payload = {
|
||||||
|
page,
|
||||||
|
per_page: perPage,
|
||||||
|
user_id: this.connectionData.userId,
|
||||||
|
extras: this.extraFields,
|
||||||
|
min_upload_date: minUploadDate,
|
||||||
|
};
|
||||||
|
const { photos } = (await this.client.photos.search(payload)).body;
|
||||||
|
|
||||||
|
return photos;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(startTime: Date) {
|
||||||
|
const minUploadDate = DateTime.fromJSDate(startTime).toSeconds().toString();
|
||||||
|
const photos = await this.getPhotos({ page: 1, minUploadDate });
|
||||||
|
const allPhotos = [...photos.photo];
|
||||||
|
|
||||||
|
for (let page = photos.page + 1; page <= photos.pages; page++) {
|
||||||
|
const photos = (await this.getPhotos({ page, minUploadDate }));
|
||||||
|
allPhotos.push(...photos.photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allPhotos;
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun(startTime: Date) {
|
||||||
|
const { photo } = await this.getPhotos({ perPage: 1 });
|
||||||
|
|
||||||
|
return photo;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,32 +0,0 @@
|
|||||||
import defineTrigger from '../../../../helpers/define-trigger';
|
|
||||||
import newPhotosInAlbum from './new-photos-in-album';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New photos in album',
|
|
||||||
pollInterval: 15,
|
|
||||||
key: 'newPhotosInAlbum',
|
|
||||||
description: 'Triggers when you add a new photo in an album.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Album',
|
|
||||||
key: 'album',
|
|
||||||
type: 'dropdown' as const,
|
|
||||||
required: true,
|
|
||||||
variables: false,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listAlbums',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
await newPhotosInAlbum($);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,56 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
|
|
||||||
const extraFields = [
|
|
||||||
'license',
|
|
||||||
'date_upload',
|
|
||||||
'date_taken',
|
|
||||||
'owner_name',
|
|
||||||
'icon_server',
|
|
||||||
'original_format',
|
|
||||||
'last_update',
|
|
||||||
'geo',
|
|
||||||
'tags',
|
|
||||||
'machine_tags',
|
|
||||||
'o_dims',
|
|
||||||
'views',
|
|
||||||
'media',
|
|
||||||
'path_alias',
|
|
||||||
'url_sq',
|
|
||||||
'url_t',
|
|
||||||
'url_s',
|
|
||||||
'url_m',
|
|
||||||
'url_o',
|
|
||||||
].join(',');
|
|
||||||
|
|
||||||
const newPhotosInAlbum = async ($: IGlobalVariable) => {
|
|
||||||
let page = 1;
|
|
||||||
let pages = 1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
const params = {
|
|
||||||
page,
|
|
||||||
per_page: 11,
|
|
||||||
user_id: $.auth.data.userId,
|
|
||||||
extras: extraFields,
|
|
||||||
photoset_id: $.step.parameters.album as string,
|
|
||||||
method: 'flickr.photosets.getPhotos',
|
|
||||||
format: 'json',
|
|
||||||
nojsoncallback: 1,
|
|
||||||
};
|
|
||||||
const response = await $.http.get('/rest', { params });
|
|
||||||
const photoset = response.data.photoset;
|
|
||||||
page = photoset.page + 1;
|
|
||||||
pages = photoset.pages;
|
|
||||||
|
|
||||||
for (const photo of photoset.photo) {
|
|
||||||
$.pushTriggerItem({
|
|
||||||
raw: photo,
|
|
||||||
meta: {
|
|
||||||
internalId: photo.id as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} while (page <= pages);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default newPhotosInAlbum;
|
|
@@ -1,13 +0,0 @@
|
|||||||
import defineTrigger from '../../../../helpers/define-trigger';
|
|
||||||
import newPhotos from './new-photos';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New photos',
|
|
||||||
pollInterval: 15,
|
|
||||||
key: 'newPhotos',
|
|
||||||
description: 'Triggers when you add a new photo.',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
await newPhotos($);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,61 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
|
|
||||||
const extraFields = [
|
|
||||||
'description',
|
|
||||||
'license',
|
|
||||||
'date_upload',
|
|
||||||
'date_taken',
|
|
||||||
'owner_name',
|
|
||||||
'icon_server',
|
|
||||||
'original_format',
|
|
||||||
'last_update',
|
|
||||||
'geo',
|
|
||||||
'tags',
|
|
||||||
'machine_tags',
|
|
||||||
'o_dims',
|
|
||||||
'views',
|
|
||||||
'media',
|
|
||||||
'path_alias',
|
|
||||||
'url_sq',
|
|
||||||
'url_t',
|
|
||||||
'url_s',
|
|
||||||
'url_q',
|
|
||||||
'url_m',
|
|
||||||
'url_n',
|
|
||||||
'url_z',
|
|
||||||
'url_c',
|
|
||||||
'url_l',
|
|
||||||
'url_o',
|
|
||||||
].join(',');
|
|
||||||
|
|
||||||
const newPhotos = async ($: IGlobalVariable) => {
|
|
||||||
let page = 1;
|
|
||||||
let pages = 1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
const params = {
|
|
||||||
page,
|
|
||||||
per_page: 500,
|
|
||||||
user_id: $.auth.data.userId,
|
|
||||||
extras: extraFields,
|
|
||||||
method: 'flickr.photos.search',
|
|
||||||
format: 'json',
|
|
||||||
nojsoncallback: 1,
|
|
||||||
};
|
|
||||||
const response = await $.http.get('/rest', { params });
|
|
||||||
const photos = response.data.photos;
|
|
||||||
page = photos.page + 1;
|
|
||||||
pages = photos.pages;
|
|
||||||
|
|
||||||
for (const photo of photos.photo) {
|
|
||||||
$.pushTriggerItem({
|
|
||||||
raw: photo,
|
|
||||||
meta: {
|
|
||||||
internalId: photo.id as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} while (page <= pages);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default newPhotos;
|
|
@@ -1,58 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action';
|
|
||||||
import getRepoOwnerAndRepo from '../../common/get-repo-owner-and-repo';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Create issue',
|
|
||||||
key: 'createIssue',
|
|
||||||
description: 'Creates a new issue.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Repo',
|
|
||||||
key: 'repo',
|
|
||||||
type: 'dropdown' as const,
|
|
||||||
required: false,
|
|
||||||
variables: false,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listRepos',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Title',
|
|
||||||
key: 'title',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Body',
|
|
||||||
key: 'body',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const repoParameter = $.step.parameters.repo as string;
|
|
||||||
const title = $.step.parameters.title as string;
|
|
||||||
const body = $.step.parameters.body as string;
|
|
||||||
|
|
||||||
if (!repoParameter) throw new Error('A repo must be set!');
|
|
||||||
if (!title) throw new Error('A title must be set!');
|
|
||||||
|
|
||||||
const { repoOwner, repo } = getRepoOwnerAndRepo(repoParameter);
|
|
||||||
const response = await $.http.post(`/repos/${repoOwner}/${repo}/issues`, {
|
|
||||||
title,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
|
|
||||||
$.setActionItem({ raw: response.data });
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,3 +0,0 @@
|
|||||||
import createIssue from './create-issue';
|
|
||||||
|
|
||||||
export default [createIssue];
|
|
@@ -1,23 +0,0 @@
|
|||||||
import { IField, IGlobalVariable } from '@automatisch/types';
|
|
||||||
import { URLSearchParams } from 'url';
|
|
||||||
|
|
||||||
export default async function generateAuthUrl($: IGlobalVariable) {
|
|
||||||
const scopes = ['read:org', 'repo', 'user'];
|
|
||||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
|
||||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
|
||||||
);
|
|
||||||
const redirectUri = oauthRedirectUrlField.value as string;
|
|
||||||
const searchParams = new URLSearchParams({
|
|
||||||
client_id: $.auth.data.consumerKey as string,
|
|
||||||
redirect_uri: redirectUri,
|
|
||||||
scope: scopes.join(','),
|
|
||||||
});
|
|
||||||
|
|
||||||
const url = `${
|
|
||||||
$.app.baseUrl
|
|
||||||
}/login/oauth/authorize?${searchParams.toString()}`;
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
url,
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,49 +0,0 @@
|
|||||||
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/github/connections/add',
|
|
||||||
placeholder: null,
|
|
||||||
description:
|
|
||||||
'When asked to input an OAuth callback or redirect URL in Github OAuth, enter the URL above.',
|
|
||||||
docUrl: 'https://automatisch.io/docs/github#oauth-redirect-url',
|
|
||||||
clickToCopy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'consumerKey',
|
|
||||||
label: 'Client ID',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
docUrl: 'https://automatisch.io/docs/github#client-id',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'consumerSecret',
|
|
||||||
label: 'Client Secret',
|
|
||||||
type: 'string' as const,
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
docUrl: 'https://automatisch.io/docs/github#client-secret',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
generateAuthUrl,
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
};
|
|
@@ -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;
|
|
@@ -1,36 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
import getCurrentUser from '../common/get-current-user';
|
|
||||||
|
|
||||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
|
||||||
const response = await $.http.post(
|
|
||||||
`${$.app.baseUrl}/login/oauth/access_token`,
|
|
||||||
{
|
|
||||||
client_id: $.auth.data.consumerKey,
|
|
||||||
client_secret: $.auth.data.consumerSecret,
|
|
||||||
code: $.auth.data.code,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
$.auth.data.accessToken = data.access_token;
|
|
||||||
|
|
||||||
const currentUser = await getCurrentUser($);
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
consumerKey: $.auth.data.consumerKey,
|
|
||||||
consumerSecret: $.auth.data.consumerSecret,
|
|
||||||
accessToken: data.access_token,
|
|
||||||
scope: data.scope,
|
|
||||||
tokenType: data.token_type,
|
|
||||||
userId: currentUser.id,
|
|
||||||
screenName: currentUser.login,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
95
packages/backend/src/apps/github/authentication.ts
Normal file
95
packages/backend/src/apps/github/authentication.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import type {
|
||||||
|
IAuthentication,
|
||||||
|
IApp,
|
||||||
|
IField,
|
||||||
|
IJSONObject,
|
||||||
|
} from '@automatisch/types';
|
||||||
|
import createHttpClient, { IHttpClient } from '../../helpers/http-client';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
export default class Authentication implements IAuthentication {
|
||||||
|
appData: IApp;
|
||||||
|
connectionData: IJSONObject;
|
||||||
|
scopes: string[] = ['read:org', 'repo', 'user'];
|
||||||
|
client: IHttpClient;
|
||||||
|
|
||||||
|
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||||
|
this.connectionData = connectionData;
|
||||||
|
this.appData = appData;
|
||||||
|
this.client = createHttpClient({ baseURL: 'https://github.com' });
|
||||||
|
}
|
||||||
|
|
||||||
|
get oauthRedirectUrl(): string {
|
||||||
|
return this.appData.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAuthData(): Promise<{ url: string }> {
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
client_id: this.connectionData.consumerKey as string,
|
||||||
|
redirect_uri: this.oauthRedirectUrl,
|
||||||
|
scope: this.scopes.join(','),
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `https://github.com/login/oauth/authorize?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyCredentials() {
|
||||||
|
const response = await this.client.post('/login/oauth/access_token', {
|
||||||
|
client_id: this.connectionData.consumerKey,
|
||||||
|
client_secret: this.connectionData.consumerSecret,
|
||||||
|
code: this.connectionData.oauthVerifier,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = Object.fromEntries(new URLSearchParams(response.data));
|
||||||
|
|
||||||
|
this.connectionData.accessToken = data.access_token;
|
||||||
|
|
||||||
|
const tokenInfo = await this.getTokenInfo();
|
||||||
|
|
||||||
|
return {
|
||||||
|
consumerKey: this.connectionData.consumerKey,
|
||||||
|
consumerSecret: this.connectionData.consumerSecret,
|
||||||
|
accessToken: data.access_token,
|
||||||
|
scope: data.scope,
|
||||||
|
tokenType: data.token_type,
|
||||||
|
userId: tokenInfo.data.user.id,
|
||||||
|
screenName: tokenInfo.data.user.login,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTokenInfo() {
|
||||||
|
const basicAuthToken = Buffer.from(
|
||||||
|
this.connectionData.consumerKey + ':' + this.connectionData.consumerSecret
|
||||||
|
).toString('base64');
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Basic ${basicAuthToken}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
access_token: this.connectionData.accessToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await this.client.post(
|
||||||
|
`https://api.github.com/applications/${this.connectionData.consumerKey}/token`,
|
||||||
|
body,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async isStillVerified() {
|
||||||
|
try {
|
||||||
|
await this.getTokenInfo();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,11 +0,0 @@
|
|||||||
import { TBeforeRequest } from '@automatisch/types';
|
|
||||||
|
|
||||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
|
||||||
if (requestConfig.headers && $.auth.data?.accessToken) {
|
|
||||||
requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessToken}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -1,10 +0,0 @@
|
|||||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
|
||||||
|
|
||||||
const getCurrentUser = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
|
||||||
const response = await $.http.get('/user');
|
|
||||||
|
|
||||||
const currentUser = response.data;
|
|
||||||
return currentUser;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getCurrentUser;
|
|
@@ -1,17 +0,0 @@
|
|||||||
type TRepoOwnerAndRepo = {
|
|
||||||
repoOwner?: string;
|
|
||||||
repo?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function getRepoOwnerAndRepo(
|
|
||||||
repoFullName: string
|
|
||||||
): TRepoOwnerAndRepo {
|
|
||||||
if (!repoFullName) return {};
|
|
||||||
|
|
||||||
const [repoOwner, repo] = repoFullName.split('/');
|
|
||||||
|
|
||||||
return {
|
|
||||||
repoOwner,
|
|
||||||
repo,
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,32 +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;
|
|
||||||
}
|
|
16
packages/backend/src/apps/github/data.ts
Normal file
16
packages/backend/src/apps/github/data.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
import ListRepos from './data/list-repos';
|
||||||
|
import ListBranches from './data/list-branches';
|
||||||
|
import ListLabels from './data/list-labels';
|
||||||
|
|
||||||
|
export default class Data {
|
||||||
|
listRepos: ListRepos;
|
||||||
|
listBranches: ListBranches;
|
||||||
|
listLabels: ListLabels;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
this.listRepos = new ListRepos(connectionData);
|
||||||
|
this.listBranches = new ListBranches(connectionData, parameters);
|
||||||
|
this.listLabels = new ListLabels(connectionData, parameters);
|
||||||
|
}
|
||||||
|
}
|
36
packages/backend/src/apps/github/data/list-branches.ts
Normal file
36
packages/backend/src/apps/github/data/list-branches.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import type { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class ListBranches {
|
||||||
|
client?: Octokit;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters?: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
owner: this.repoOwner,
|
||||||
|
repo: this.repo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
const branches = await this.client.paginate(this.client.rest.repos.listBranches, this.options);
|
||||||
|
|
||||||
|
return branches.map((branch) => ({
|
||||||
|
value: branch.name,
|
||||||
|
name: branch.name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
36
packages/backend/src/apps/github/data/list-labels.ts
Normal file
36
packages/backend/src/apps/github/data/list-labels.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import type { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class ListLabels {
|
||||||
|
client?: Octokit;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters?: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
owner: this.repoOwner,
|
||||||
|
repo: this.repo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
const labels = await this.client.paginate(this.client.rest.issues.listLabelsForRepo, this.options);
|
||||||
|
|
||||||
|
return labels.map((label) => ({
|
||||||
|
value: label.name,
|
||||||
|
name: label.name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
23
packages/backend/src/apps/github/data/list-repos.ts
Normal file
23
packages/backend/src/apps/github/data/list-repos.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import type { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
export default class ListRepos {
|
||||||
|
client?: Octokit;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
const repos = await this.client.paginate(this.client.rest.repos.listForAuthenticatedUser);
|
||||||
|
|
||||||
|
return repos.map((repo) => ({
|
||||||
|
value: repo.full_name,
|
||||||
|
name: repo.full_name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +0,0 @@
|
|||||||
import listLabels from './list-labels';
|
|
||||||
import listRepos from './list-repos';
|
|
||||||
|
|
||||||
export default [listLabels, listRepos];
|
|
@@ -1,28 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
import getRepoOwnerAndRepo from '../../common/get-repo-owner-and-repo';
|
|
||||||
import paginateAll from '../../common/paginate-all';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'List labels',
|
|
||||||
key: 'listLabels',
|
|
||||||
|
|
||||||
async run($: IGlobalVariable) {
|
|
||||||
const { repoOwner, repo } = getRepoOwnerAndRepo(
|
|
||||||
$.step.parameters.repo as string
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!repo) return { data: [] };
|
|
||||||
|
|
||||||
const firstPageRequest = $.http.get(`/repos/${repoOwner}/${repo}/labels`);
|
|
||||||
const response = await paginateAll($, firstPageRequest);
|
|
||||||
|
|
||||||
response.data = response.data.map((repo: { name: string }) => {
|
|
||||||
return {
|
|
||||||
value: repo.name,
|
|
||||||
name: repo.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,21 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
import paginateAll from '../../common/paginate-all';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'List repos',
|
|
||||||
key: 'listRepos',
|
|
||||||
|
|
||||||
async run($: IGlobalVariable) {
|
|
||||||
const firstPageRequest = $.http.get('/user/repos');
|
|
||||||
const response = await paginateAll($, firstPageRequest);
|
|
||||||
|
|
||||||
response.data = response.data.map((repo: { full_name: string }) => {
|
|
||||||
return {
|
|
||||||
value: repo.full_name,
|
|
||||||
name: repo.full_name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,22 +1,25 @@
|
|||||||
import defineApp from '../../helpers/define-app';
|
import {
|
||||||
import addAuthHeader from './common/add-auth-header';
|
IService,
|
||||||
import auth from './auth';
|
IAuthentication,
|
||||||
import triggers from './triggers';
|
IApp,
|
||||||
import actions from './actions';
|
IJSONObject,
|
||||||
import dynamicData from './dynamic-data';
|
} from '@automatisch/types';
|
||||||
|
import Authentication from './authentication';
|
||||||
|
import Triggers from './triggers';
|
||||||
|
import Data from './data';
|
||||||
|
|
||||||
export default defineApp({
|
export default class Github implements IService {
|
||||||
name: 'Github',
|
authenticationClient: IAuthentication;
|
||||||
key: 'github',
|
triggers: Triggers;
|
||||||
baseUrl: 'https://github.com',
|
data: Data;
|
||||||
apiBaseUrl: 'https://api.github.com',
|
|
||||||
iconUrl: '{BASE_URL}/apps/github/assets/favicon.svg',
|
constructor(
|
||||||
authDocUrl: 'https://automatisch.io/docs/apps/github/connection',
|
appData: IApp,
|
||||||
primaryColor: '000000',
|
connectionData: IJSONObject,
|
||||||
supportsConnections: true,
|
parameters: IJSONObject
|
||||||
beforeRequest: [addAuthHeader],
|
) {
|
||||||
auth,
|
this.authenticationClient = new Authentication(appData, connectionData);
|
||||||
triggers,
|
this.data = new Data(connectionData, parameters);
|
||||||
actions,
|
this.triggers = new Triggers(connectionData, parameters);
|
||||||
dynamicData,
|
}
|
||||||
});
|
}
|
||||||
|
747
packages/backend/src/apps/github/info.json
Normal file
747
packages/backend/src/apps/github/info.json
Normal file
@@ -0,0 +1,747 @@
|
|||||||
|
{
|
||||||
|
"name": "Github",
|
||||||
|
"key": "github",
|
||||||
|
"iconUrl": "{BASE_URL}/apps/github/assets/favicon.svg",
|
||||||
|
"docUrl": "https://automatisch.io/docs/github",
|
||||||
|
"primaryColor": "000000",
|
||||||
|
"supportsConnections": true,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"key": "oAuthRedirectUrl",
|
||||||
|
"label": "OAuth Redirect URL",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": true,
|
||||||
|
"value": "{WEB_APP_URL}/app/github/connections/add",
|
||||||
|
"placeholder": null,
|
||||||
|
"description": "When asked to input an OAuth callback or redirect URL in Github OAuth, enter the URL above.",
|
||||||
|
"docUrl": "https://automatisch.io/docs/github#oauth-redirect-url",
|
||||||
|
"clickToCopy": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "consumerKey",
|
||||||
|
"label": "Client ID",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": false,
|
||||||
|
"value": null,
|
||||||
|
"placeholder": null,
|
||||||
|
"description": null,
|
||||||
|
"docUrl": "https://automatisch.io/docs/github#client-id",
|
||||||
|
"clickToCopy": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "consumerSecret",
|
||||||
|
"label": "Client Secret",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"readOnly": false,
|
||||||
|
"value": null,
|
||||||
|
"placeholder": null,
|
||||||
|
"description": null,
|
||||||
|
"docUrl": "https://automatisch.io/docs/github#client-secret",
|
||||||
|
"clickToCopy": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"authenticationSteps": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "{key}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "consumerKey",
|
||||||
|
"value": "{fields.consumerKey}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "consumerSecret",
|
||||||
|
"value": "{fields.consumerSecret}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createAuthData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"type": "openWithPopup",
|
||||||
|
"name": "openAuthPopup",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"value": "{createAuthData.url}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "oauthVerifier",
|
||||||
|
"value": "{openAuthPopup.code}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "verifyConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{createConnection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reconnectionSteps": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "resetConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "consumerKey",
|
||||||
|
"value": "{fields.consumerKey}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "consumerSecret",
|
||||||
|
"value": "{fields.consumerSecret}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "createAuthData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"type": "openWithPopup",
|
||||||
|
"name": "openAuthPopup",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "url",
|
||||||
|
"value": "{createAuthData.url}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "updateConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "formattedData",
|
||||||
|
"value": null,
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "oauthVerifier",
|
||||||
|
"value": "{openAuthPopup.code}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 6,
|
||||||
|
"type": "mutation",
|
||||||
|
"name": "verifyConnection",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "{connection.id}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"triggers": [
|
||||||
|
{
|
||||||
|
"name": "New repository",
|
||||||
|
"key": "newRepository",
|
||||||
|
"description": "Triggers when a new repository is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New organization",
|
||||||
|
"key": "newOrganization",
|
||||||
|
"description": "Triggers when a new organization is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New branch",
|
||||||
|
"key": "newBranch",
|
||||||
|
"description": "Triggers when a new branch is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New notification",
|
||||||
|
"key": "newNotification",
|
||||||
|
"description": "Triggers when a new notification is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": false,
|
||||||
|
"variables": false,
|
||||||
|
"description": "If blank, we will retrieve all notifications.",
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New pull request",
|
||||||
|
"key": "newPullRequest",
|
||||||
|
"description": "Triggers when a new pull request is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New watcher",
|
||||||
|
"key": "newWatcher",
|
||||||
|
"description": "Triggers when a new watcher is added to a repo",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New milestone",
|
||||||
|
"key": "newMilestone",
|
||||||
|
"description": "Triggers when a new milestone is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New commit comment",
|
||||||
|
"key": "newCommitComment",
|
||||||
|
"description": "Triggers when a new commit comment is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New label",
|
||||||
|
"key": "newLabel",
|
||||||
|
"description": "Triggers when a new label is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New collaborator",
|
||||||
|
"key": "newCollaborator",
|
||||||
|
"description": "Triggers when a new collaborator is added to a repo",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New release",
|
||||||
|
"key": "newRelease",
|
||||||
|
"description": "Triggers when a new release is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New commit",
|
||||||
|
"key": "newCommit",
|
||||||
|
"description": "Triggers when a new commit is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Head",
|
||||||
|
"key": "head",
|
||||||
|
"type": "dropdown",
|
||||||
|
"description": "Branch to pull commits from. If unspecified, will use the repository's default branch (usually main or develop).",
|
||||||
|
"required": false,
|
||||||
|
"variables": false,
|
||||||
|
"dependsOn": ["parameters.repo"],
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listBranches"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "parameters.repo",
|
||||||
|
"value": "{parameters.repo}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New issue",
|
||||||
|
"key": "newIssue",
|
||||||
|
"description": "Triggers when a new issue is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseConnection",
|
||||||
|
"name": "Choose connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": false,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Which types of issues should this trigger on?",
|
||||||
|
"key": "issueType",
|
||||||
|
"type": "dropdown",
|
||||||
|
"description": "Defaults to any issue you can see.",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"value": "all",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Any issue you can see",
|
||||||
|
"value": "all"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Only issues assigned to you",
|
||||||
|
"value": "assigned"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Only issues created by you",
|
||||||
|
"value": "created"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Only issues you're mentioned in",
|
||||||
|
"value": "mentioned"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Only issues you're subscribed to",
|
||||||
|
"value": "subscribed"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Label",
|
||||||
|
"key": "label",
|
||||||
|
"type": "dropdown",
|
||||||
|
"description": "Only trigger on issues when this label is added.",
|
||||||
|
"required": false,
|
||||||
|
"variables": false,
|
||||||
|
"dependsOn": ["parameters.repo"],
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listLabels"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "parameters.repo",
|
||||||
|
"value": "{parameters.repo}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
46
packages/backend/src/apps/github/triggers.ts
Normal file
46
packages/backend/src/apps/github/triggers.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
import NewRepository from './triggers/new-repository';
|
||||||
|
import NewOrganization from './triggers/new-organization';
|
||||||
|
import NewBranch from './triggers/new-branch';
|
||||||
|
import NewNotification from './triggers/new-notification';
|
||||||
|
import NewPullRequest from './triggers/new-pull-request';
|
||||||
|
import NewWatcher from './triggers/new-watcher';
|
||||||
|
import NewMilestone from './triggers/new-milestone';
|
||||||
|
import NewCommit from './triggers/new-commit';
|
||||||
|
import NewCommitComment from './triggers/new-commit-comment';
|
||||||
|
import NewLabel from './triggers/new-label';
|
||||||
|
import NewCollaborator from './triggers/new-collaborator';
|
||||||
|
import NewRelease from './triggers/new-release';
|
||||||
|
import NewIssue from './triggers/new-issue';
|
||||||
|
|
||||||
|
export default class Triggers {
|
||||||
|
newRepository: NewRepository;
|
||||||
|
newOrganization: NewOrganization;
|
||||||
|
newBranch: NewBranch;
|
||||||
|
newNotification: NewNotification;
|
||||||
|
newPullRequest: NewPullRequest;
|
||||||
|
newWatcher: NewWatcher;
|
||||||
|
newMilestone: NewMilestone;
|
||||||
|
newCommit: NewCommit;
|
||||||
|
newCommitComment: NewCommitComment;
|
||||||
|
newLabel: NewLabel;
|
||||||
|
newCollaborator: NewCollaborator;
|
||||||
|
newRelease: NewRelease;
|
||||||
|
newIssue: NewIssue;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
this.newRepository = new NewRepository(connectionData);
|
||||||
|
this.newOrganization = new NewOrganization(connectionData);
|
||||||
|
this.newBranch = new NewBranch(connectionData, parameters);
|
||||||
|
this.newNotification = new NewNotification(connectionData, parameters);
|
||||||
|
this.newPullRequest = new NewPullRequest(connectionData, parameters);
|
||||||
|
this.newWatcher = new NewWatcher(connectionData, parameters);
|
||||||
|
this.newMilestone = new NewMilestone(connectionData, parameters);
|
||||||
|
this.newCommit = new NewCommit(connectionData, parameters);
|
||||||
|
this.newCommitComment = new NewCommitComment(connectionData, parameters);
|
||||||
|
this.newLabel = new NewLabel(connectionData, parameters);
|
||||||
|
this.newCollaborator = new NewCollaborator(connectionData, parameters);
|
||||||
|
this.newRelease = new NewRelease(connectionData, parameters);
|
||||||
|
this.newIssue = new NewIssue(connectionData, parameters);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +0,0 @@
|
|||||||
import newIssues from './new-issues';
|
|
||||||
import newPullRequests from './new-pull-requests';
|
|
||||||
import newStargazers from './new-stargazers';
|
|
||||||
import newWatchers from './new-watchers';
|
|
||||||
|
|
||||||
export default [newIssues, newPullRequests, newStargazers, newWatchers];
|
|
42
packages/backend/src/apps/github/triggers/new-branch.ts
Normal file
42
packages/backend/src/apps/github/triggers/new-branch.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class NewBranch {
|
||||||
|
client?: Octokit;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
owner: this.repoOwner,
|
||||||
|
repo: this.repo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
// TODO: implement pagination on undated entires
|
||||||
|
return await this.client.paginate(this.client.rest.repos.listBranches, this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const options = {
|
||||||
|
...this.options,
|
||||||
|
per_page: 1,
|
||||||
|
};
|
||||||
|
const { data: branches } = await this.client.rest.repos.listBranches(options);
|
||||||
|
|
||||||
|
return branches;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class NewCollaborator {
|
||||||
|
client?: Octokit;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
owner: this.repoOwner,
|
||||||
|
repo: this.repo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
// TODO: implement pagination on undated entries
|
||||||
|
return await this.client.paginate(
|
||||||
|
this.client.rest.repos.listCollaborators,
|
||||||
|
this.options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const options = {
|
||||||
|
...this.options,
|
||||||
|
per_page: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (await this.client.rest.repos.listCollaborators(options)).data;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class NewCommitComment {
|
||||||
|
client?: Octokit;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
owner: this.repoOwner,
|
||||||
|
repo: this.repo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(startTime: Date) {
|
||||||
|
const iterator = await this.client.paginate.iterator(this.client.rest.repos.listCommitCommentsForRepo, this.options);
|
||||||
|
const newCommitComments = [];
|
||||||
|
|
||||||
|
const startTimeDateObject = DateTime.fromJSDate(startTime);
|
||||||
|
|
||||||
|
commitCommentIterator:
|
||||||
|
for await (const { data: commitComments } of iterator) {
|
||||||
|
for (const commitComment of commitComments) {
|
||||||
|
const createdAtDateObject = DateTime.fromISO(commitComment.created_at);
|
||||||
|
|
||||||
|
if (createdAtDateObject < startTimeDateObject) {
|
||||||
|
break commitCommentIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
newCommitComments.push(commitComment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCommitComments;
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const options = {
|
||||||
|
...this.options,
|
||||||
|
per_page: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (await this.client.rest.repos.listCommitCommentsForRepo(options)).data;
|
||||||
|
}
|
||||||
|
}
|
59
packages/backend/src/apps/github/triggers/new-commit.ts
Normal file
59
packages/backend/src/apps/github/triggers/new-commit.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class NewCommit {
|
||||||
|
client?: Octokit;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
head?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters?.head) {
|
||||||
|
this.head = parameters.head as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
const options = {
|
||||||
|
owner: this.repoOwner,
|
||||||
|
repo: this.repo,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.head) {
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
sha: this.head,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(startTime: Date) {
|
||||||
|
const options = {
|
||||||
|
...this.options,
|
||||||
|
since: DateTime.fromJSDate(startTime).toISO(),
|
||||||
|
};
|
||||||
|
return await this.client.paginate(this.client.rest.repos.listCommits, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const options = {
|
||||||
|
...this.options,
|
||||||
|
per_page: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (await this.client.rest.repos.listCommits(options)).data;
|
||||||
|
}
|
||||||
|
}
|
88
packages/backend/src/apps/github/triggers/new-issue.ts
Normal file
88
packages/backend/src/apps/github/triggers/new-issue.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class NewIssue {
|
||||||
|
client?: Octokit;
|
||||||
|
connectionData?: IJSONObject;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
hasRepo?: boolean;
|
||||||
|
label?: string;
|
||||||
|
issueType?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
labels: this.label,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async listRepoIssues(options = {}, paginate = false) {
|
||||||
|
const listRepoIssues = this.client.rest.issues.listForRepo;
|
||||||
|
|
||||||
|
const extendedOptions = {
|
||||||
|
...this.options,
|
||||||
|
repo: this.repo,
|
||||||
|
owner: this.repoOwner,
|
||||||
|
filter: this.issueType,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (paginate) {
|
||||||
|
return await this.client.paginate(listRepoIssues, extendedOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await listRepoIssues(extendedOptions)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async listIssues(options = {}, paginate = false) {
|
||||||
|
const listIssues = this.client.rest.issues.listForAuthenticatedUser;
|
||||||
|
|
||||||
|
const extendedOptions = {
|
||||||
|
...this.options,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (paginate) {
|
||||||
|
return await this.client.paginate(listIssues, extendedOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await listIssues(extendedOptions)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(startTime: Date) {
|
||||||
|
const options = {
|
||||||
|
since: DateTime.fromJSDate(startTime).toISO(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.hasRepo) {
|
||||||
|
return await this.listRepoIssues(options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.listIssues(options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const options = {
|
||||||
|
per_page: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.hasRepo) {
|
||||||
|
return await this.listRepoIssues(options, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.listIssues(options, false);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,86 +0,0 @@
|
|||||||
import defineTrigger from '../../../../helpers/define-trigger';
|
|
||||||
import newIssues from './new-issues';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New issues',
|
|
||||||
key: 'newIssues',
|
|
||||||
pollInterval: 15,
|
|
||||||
description: 'Triggers when a new issue is created',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Repo',
|
|
||||||
key: 'repo',
|
|
||||||
type: 'dropdown' as const,
|
|
||||||
required: false,
|
|
||||||
variables: false,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listRepos',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Which types of issues should this trigger on?',
|
|
||||||
key: 'issueType',
|
|
||||||
type: 'dropdown' as const,
|
|
||||||
description: 'Defaults to any issue you can see.',
|
|
||||||
required: true,
|
|
||||||
variables: false,
|
|
||||||
value: 'all',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Any issue you can see',
|
|
||||||
value: 'all',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Only issues assigned to you',
|
|
||||||
value: 'assigned',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Only issues created by you',
|
|
||||||
value: 'created',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: `Only issues you're mentioned in`,
|
|
||||||
value: 'mentioned',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: `Only issues you're subscribed to`,
|
|
||||||
value: 'subscribed',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Label',
|
|
||||||
key: 'label',
|
|
||||||
type: 'dropdown' as const,
|
|
||||||
description: 'Only trigger on issues when this label is added.',
|
|
||||||
required: false,
|
|
||||||
variables: false,
|
|
||||||
dependsOn: ['parameters.repo'],
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listLabels',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.repo',
|
|
||||||
value: '{parameters.repo}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
await newIssues($);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,50 +0,0 @@
|
|||||||
import { IGlobalVariable } from '@automatisch/types';
|
|
||||||
import getRepoOwnerAndRepo from '../../common/get-repo-owner-and-repo';
|
|
||||||
import parseLinkHeader from '../../../../helpers/parse-header-link';
|
|
||||||
|
|
||||||
function getPathname($: IGlobalVariable) {
|
|
||||||
const { repoOwner, repo } = getRepoOwnerAndRepo(
|
|
||||||
$.step.parameters.repo as string
|
|
||||||
);
|
|
||||||
|
|
||||||
if (repoOwner && repo) {
|
|
||||||
return `/repos/${repoOwner}/${repo}/issues`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '/issues';
|
|
||||||
}
|
|
||||||
|
|
||||||
const newIssues = async ($: IGlobalVariable) => {
|
|
||||||
const pathname = getPathname($);
|
|
||||||
const params = {
|
|
||||||
labels: $.step.parameters.label,
|
|
||||||
filter: 'all',
|
|
||||||
state: 'all',
|
|
||||||
sort: 'created',
|
|
||||||
direction: 'desc',
|
|
||||||
per_page: 100,
|
|
||||||
};
|
|
||||||
|
|
||||||
let links;
|
|
||||||
do {
|
|
||||||
const response = await $.http.get(pathname, { params });
|
|
||||||
links = parseLinkHeader(response.headers.link);
|
|
||||||
|
|
||||||
if (response.data.length) {
|
|
||||||
for (const issue of response.data) {
|
|
||||||
const issueId = issue.id;
|
|
||||||
|
|
||||||
const dataItem = {
|
|
||||||
raw: issue,
|
|
||||||
meta: {
|
|
||||||
internalId: issueId.toString(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (links.next);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default newIssues;
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user