Compare commits
121 Commits
update-sam
...
formatter-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0b8e33f96d | ||
![]() |
c193f9334f | ||
![]() |
6e682dc752 | ||
![]() |
da86fe56bd | ||
![]() |
45865d701a | ||
![]() |
a66a31b474 | ||
![]() |
2661e7102f | ||
![]() |
224965b91e | ||
![]() |
a9c7375534 | ||
![]() |
e77f7ee0bf | ||
![]() |
ae5dd0cad6 | ||
![]() |
a128907a4e | ||
![]() |
d6453a8ed0 | ||
![]() |
dd1e8240b8 | ||
![]() |
b12f39916f | ||
![]() |
aae88fe1ad | ||
![]() |
83bb400df1 | ||
![]() |
8ea8067788 | ||
![]() |
9fbc9d59f5 | ||
![]() |
b96ba69a72 | ||
![]() |
c4ccab6a5d | ||
![]() |
f84f27bb56 | ||
![]() |
416cc0ffa9 | ||
![]() |
1fd5ec4db6 | ||
![]() |
4795c35c68 | ||
![]() |
25ce63b86d | ||
![]() |
5271033d34 | ||
![]() |
6ba8f33399 | ||
![]() |
7ab79bd815 | ||
![]() |
04a0a847c7 | ||
![]() |
436fa9af69 | ||
![]() |
ca0bbb0f08 | ||
![]() |
88996144a5 | ||
![]() |
44d5eee99e | ||
![]() |
0d1ff6074f | ||
![]() |
d63757634a | ||
![]() |
fd61cf3388 | ||
![]() |
a6a6b63e5a | ||
![]() |
c02c2def29 | ||
![]() |
ff66548462 | ||
![]() |
c9f292e252 | ||
![]() |
18cef5f3bd | ||
![]() |
e19340f1e0 | ||
![]() |
feb613cb6d | ||
![]() |
afa6bdfa44 | ||
![]() |
200e6d9905 | ||
![]() |
70772c49bd | ||
![]() |
762ea97e8b | ||
![]() |
8156b8b356 | ||
![]() |
3a2cbae0a0 | ||
![]() |
0ad8da097b | ||
![]() |
e2dcdd2811 | ||
![]() |
8074f9146b | ||
![]() |
df24bac913 | ||
![]() |
4d4091adcc | ||
![]() |
cac54c41a1 | ||
![]() |
130931d7af | ||
![]() |
d35b08b35e | ||
![]() |
82031da6a6 | ||
![]() |
9df5ee7b11 | ||
![]() |
2ed1a57cd9 | ||
![]() |
101450cba6 | ||
![]() |
6bab5b3f7c | ||
![]() |
ca3c0e00a7 | ||
![]() |
6d64daf324 | ||
![]() |
9ae4578e19 | ||
![]() |
e06b7ab87a | ||
![]() |
1e2adedcbf | ||
![]() |
adf763c1b0 | ||
![]() |
36ee0df256 | ||
![]() |
823d85b24a | ||
![]() |
a3b3038709 | ||
![]() |
ddeb18f626 | ||
![]() |
90cd11bd38 | ||
![]() |
e9ba37b8de | ||
![]() |
d5e4a1b1ad | ||
![]() |
129e6d60e5 | ||
![]() |
4b77f2f590 | ||
![]() |
a909966562 | ||
![]() |
fd184239d6 | ||
![]() |
52bc49dc6a | ||
![]() |
b9352ccc06 | ||
![]() |
525b2baf06 | ||
![]() |
a8edeb2459 | ||
![]() |
e3830d64e0 | ||
![]() |
91f3e2c2b4 | ||
![]() |
77b4408416 | ||
![]() |
cede96f018 | ||
![]() |
8e0a28d238 | ||
![]() |
da5d594428 | ||
![]() |
9f9ee0bb58 | ||
![]() |
163aca6179 | ||
![]() |
cb06d3b0ae | ||
![]() |
dbe18dd100 | ||
![]() |
217970667a | ||
![]() |
dace794167 | ||
![]() |
590780a539 | ||
![]() |
cbd1f47e87 | ||
![]() |
f89cff4e4a | ||
![]() |
cb08e0bf9f | ||
![]() |
3b54b29a99 | ||
![]() |
25983e046c | ||
![]() |
a6a124d2e6 | ||
![]() |
c7e1d30553 | ||
![]() |
6cc8c45634 | ||
![]() |
ee9a9114b7 | ||
![]() |
11f00f866c | ||
![]() |
03ea61ba81 | ||
![]() |
f6c500c998 | ||
![]() |
b590f0f98f | ||
![]() |
ef9359b208 | ||
![]() |
efd243a340 | ||
![]() |
bafb8b86db | ||
![]() |
84b701747f | ||
![]() |
ec42446daa | ||
![]() |
5046c4c911 | ||
![]() |
ce8c9906cb | ||
![]() |
6fb5482bba | ||
![]() |
58189963f5 | ||
![]() |
f488a71304 | ||
![]() |
4b706e004d |
@@ -29,7 +29,6 @@ rm -rf .env
|
||||
echo "
|
||||
PORT=$WEB_PORT
|
||||
REACT_APP_GRAPHQL_URL=http://localhost:$BACKEND_PORT/graphql
|
||||
REACT_APP_NOTIFICATIONS_URL=https://notifications.automatisch.io
|
||||
" >> .env
|
||||
cd $CURRENT_DIR
|
||||
|
||||
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -1,5 +1,11 @@
|
||||
name: Automatisch CI
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
linter:
|
||||
runs-on: ubuntu-latest
|
||||
|
88
.github/workflows/playwright.yml
vendored
Normal file
88
.github/workflows/playwright.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: Automatisch UI Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
ENCRYPTION_KEY: sample_encryption_key
|
||||
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
||||
APP_SECRET_KEY: sample_app_secret_key
|
||||
POSTGRES_HOST: localhost
|
||||
POSTGRES_DATABASE: automatisch
|
||||
POSTGRES_PORT: 5432
|
||||
POSTGRES_USERNAME: automatisch_user
|
||||
POSTGRES_PASSWORD: automatisch_password
|
||||
REDIS_HOST: localhost
|
||||
APP_ENV: production
|
||||
LICENSE_KEY: ${{ secrets.E2E_LICENSE_KEY }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14.5-alpine
|
||||
env:
|
||||
POSTGRES_DB: automatisch
|
||||
POSTGRES_USER: automatisch_user
|
||||
POSTGRES_PASSWORD: automatisch_password
|
||||
options: >-
|
||||
--health-cmd "pg_isready -U automatisch_user -d automatisch"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis:7.0.4-alpine
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: yarn && yarn lerna bootstrap
|
||||
- name: Install Playwright Browsers
|
||||
run: yarn playwright install --with-deps
|
||||
- name: Build Automatisch
|
||||
run: yarn lerna run --scope=@*/{web,backend,cli} build
|
||||
env:
|
||||
# Keep this until we clean up warnings in build processes
|
||||
CI: false
|
||||
- name: Migrate database
|
||||
working-directory: ./packages/backend
|
||||
run: yarn db:migrate --migrations-directory ./dist/src/db/migrations
|
||||
- name: Seed user
|
||||
working-directory: ./packages/backend
|
||||
run: yarn db:seed:user &
|
||||
- name: Run Automatisch
|
||||
run: yarn start &
|
||||
working-directory: ./packages/backend
|
||||
- name: Run Automatisch worker
|
||||
run: node dist/src/worker.js &
|
||||
working-directory: ./packages/backend
|
||||
- name: Run Playwright tests
|
||||
working-directory: ./packages/e2e-tests
|
||||
env:
|
||||
LOGIN_EMAIL: user@automatisch.io
|
||||
LOGIN_PASSWORD: sample
|
||||
BASE_URL: http://localhost:3000
|
||||
run: yarn test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: ./packages/e2e-tests/test-results/**/*
|
||||
retention-days: 30
|
1
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
||||
16.15.0
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ WORKDIR /automatisch
|
||||
|
||||
RUN \
|
||||
apk --no-cache add --virtual build-dependencies python3 build-base && \
|
||||
yarn global add @automatisch/cli@0.8.0 --network-timeout 1000000 && \
|
||||
yarn global add @automatisch/cli@0.9.3 --network-timeout 1000000 && \
|
||||
rm -rf /usr/local/share/.cache/ && \
|
||||
apk del build-dependencies
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM automatischio/automatisch:0.8.0
|
||||
FROM automatischio/automatisch:0.9.3
|
||||
WORKDIR /automatisch
|
||||
|
||||
RUN apk add --no-cache openssl dos2unix
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "0.8.0",
|
||||
"version": "0.9.3",
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"command": {
|
||||
|
16
packages/backend/.env-example.test
Normal file
16
packages/backend/.env-example.test
Normal file
@@ -0,0 +1,16 @@
|
||||
APP_ENV=test
|
||||
HOST=localhost
|
||||
PROTOCOL=http
|
||||
PORT=3000
|
||||
LOG_LEVEL=debug
|
||||
WEBHOOK_SECRET_KEY=secret
|
||||
POSTGRES_DATABASE=automatisch_test
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_USERNAME=automatisch_test_user
|
||||
POSTGRES_PASSWORD=
|
||||
POSTGRES_ENABLE_SSL=false
|
||||
ENCRYPTION_KEY=secret
|
||||
APP_SECRET_KEY=secret
|
||||
REDIS_PORT=6379
|
||||
REDIS_HOST=127.0.0.1
|
5
packages/backend/ava.config.mjs
Normal file
5
packages/backend/ava.config.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
require: ['ts-node/register', './src/config/app.ts'],
|
||||
files: ['**/*.test.ts'],
|
||||
extensions: ['ts'],
|
||||
};
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@automatisch/backend",
|
||||
"version": "0.8.0",
|
||||
"version": "0.9.3",
|
||||
"license": "See LICENSE file",
|
||||
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
||||
"scripts": {
|
||||
@@ -9,7 +9,8 @@
|
||||
"build": "tsc && yarn copy-statics",
|
||||
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
|
||||
"start": "node dist/src/server.js",
|
||||
"test": "ava",
|
||||
"pretest": "APP_ENV=test ts-node ./test/setup/prepare-test-env.ts",
|
||||
"test": "APP_ENV=test ava",
|
||||
"lint": "eslint . --ignore-path ../../.eslintignore",
|
||||
"db:create": "ts-node ./bin/database/create.ts",
|
||||
"db:seed:user": "ts-node ./bin/database/seed-user.ts",
|
||||
@@ -22,7 +23,7 @@
|
||||
"prebuild": "rm -rf ./dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@automatisch/web": "^0.8.0",
|
||||
"@automatisch/web": "^0.9.3",
|
||||
"@bull-board/express": "^3.10.1",
|
||||
"@casl/ability": "^6.5.0",
|
||||
"@graphql-tools/graphql-file-loader": "^7.3.4",
|
||||
@@ -31,9 +32,11 @@
|
||||
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
||||
"@sentry/node": "^7.42.0",
|
||||
"@sentry/tracing": "^7.42.0",
|
||||
"@types/accounting": "^0.4.2",
|
||||
"@types/luxon": "^2.3.1",
|
||||
"@types/passport": "^1.0.12",
|
||||
"@types/xmlrpc": "^1.3.7",
|
||||
"accounting": "^0.4.1",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"axios": "0.24.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
@@ -62,12 +65,15 @@
|
||||
"memory-cache": "^0.2.0",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"node-html-markdown": "^1.3.0",
|
||||
"nodemailer": "6.7.0",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"objection": "^3.0.0",
|
||||
"passport": "^0.6.0",
|
||||
"pg": "^8.7.1",
|
||||
"php-serialize": "^4.0.2",
|
||||
"pluralize": "^8.0.0",
|
||||
"showdown": "^2.1.0",
|
||||
"stripe": "^11.13.0",
|
||||
"winston": "^3.7.1",
|
||||
"xmlrpc": "^1.3.2"
|
||||
@@ -108,7 +114,7 @@
|
||||
"url": "https://github.com/automatisch/automatisch/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@automatisch/types": "^0.8.0",
|
||||
"@automatisch/types": "^0.9.3",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/bull": "^3.15.8",
|
||||
"@types/cors": "^2.8.12",
|
||||
@@ -124,23 +130,14 @@
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/pg": "^8.6.1",
|
||||
"@types/pino": "^7.0.5",
|
||||
"ava": "^3.15.0",
|
||||
"@types/pluralize": "^0.0.30",
|
||||
"@types/showdown": "^2.0.1",
|
||||
"ava": "^5.3.1",
|
||||
"nodemon": "^2.0.13",
|
||||
"sinon": "^11.1.2",
|
||||
"ts-node": "^10.2.1",
|
||||
"ts-node-dev": "^1.1.8"
|
||||
},
|
||||
"ava": {
|
||||
"files": [
|
||||
"test/**/*"
|
||||
],
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
import formatDateTime from './transformers/format-date-time';
|
||||
|
||||
const transformers = {
|
||||
formatDateTime,
|
||||
};
|
||||
|
||||
export default defineAction({
|
||||
name: 'Date / Time',
|
||||
key: 'date-time',
|
||||
description: 'Perform date and time related transformations on your data.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Transform',
|
||||
key: 'transform',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
options: [{ label: 'Format Date / Time', value: 'formatDateTime' }],
|
||||
additionalFields: {
|
||||
type: 'query',
|
||||
name: 'getDynamicFields',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listTransformOptions',
|
||||
},
|
||||
{
|
||||
name: 'parameters.transform',
|
||||
value: '{parameters.transform}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const transformerName = $.step.parameters
|
||||
.transform as keyof typeof transformers;
|
||||
const output = transformers[transformerName]($);
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
output,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
@@ -0,0 +1,23 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
const formatDateTime = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
|
||||
const fromFormat = $.step.parameters.fromFormat as string;
|
||||
const fromTimezone = $.step.parameters.fromTimezone as string;
|
||||
|
||||
const inputDateTime = DateTime.fromFormat(input, fromFormat, {
|
||||
zone: fromTimezone,
|
||||
setZone: true,
|
||||
});
|
||||
|
||||
const toFormat = $.step.parameters.toFormat as string;
|
||||
const toTimezone = $.step.parameters.toTimezone as string;
|
||||
|
||||
const outputDateTime = inputDateTime.setZone(toTimezone).toFormat(toFormat);
|
||||
|
||||
return outputDateTime;
|
||||
};
|
||||
|
||||
export default formatDateTime;
|
6
packages/backend/src/apps/formatter/actions/index.ts
Normal file
6
packages/backend/src/apps/formatter/actions/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import text from './text';
|
||||
import numbers from './numbers';
|
||||
import dateTime from './date-time';
|
||||
import utilities from './utilities';
|
||||
|
||||
export default [text, numbers, dateTime, utilities];
|
58
packages/backend/src/apps/formatter/actions/numbers/index.ts
Normal file
58
packages/backend/src/apps/formatter/actions/numbers/index.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
import performMathOperation from './transformers/perform-math-operation';
|
||||
import randomNumber from './transformers/random-number';
|
||||
import formatNumber from './transformers/format-number';
|
||||
|
||||
const transformers = {
|
||||
performMathOperation,
|
||||
randomNumber,
|
||||
formatNumber,
|
||||
};
|
||||
|
||||
export default defineAction({
|
||||
name: 'Numbers',
|
||||
key: 'numbers',
|
||||
description:
|
||||
'Transform numbers to perform math operations, generate random numbers, format numbers, and much more.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Transform',
|
||||
key: 'transform',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
options: [
|
||||
{ label: 'Perform Math Operation', value: 'performMathOperation' },
|
||||
{ label: 'Random Number', value: 'randomNumber' },
|
||||
{ label: 'Format Number', value: 'formatNumber' },
|
||||
],
|
||||
additionalFields: {
|
||||
type: 'query',
|
||||
name: 'getDynamicFields',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listTransformOptions',
|
||||
},
|
||||
{
|
||||
name: 'parameters.transform',
|
||||
value: '{parameters.transform}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const transformerName = $.step.parameters
|
||||
.transform as keyof typeof transformers;
|
||||
const output = transformers[transformerName]($);
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
output,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
@@ -0,0 +1,28 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import accounting from 'accounting';
|
||||
|
||||
const formatNumber = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
const inputDecimalMark = $.step.parameters.inputDecimalMark as string;
|
||||
const toFormat = $.step.parameters.toFormat as string;
|
||||
|
||||
const normalizedNumber = accounting.unformat(input, inputDecimalMark);
|
||||
const decimalPart = normalizedNumber.toString().split('.')[1];
|
||||
const precision = decimalPart ? decimalPart.length : 0;
|
||||
|
||||
if (toFormat === '0') {
|
||||
// Comma for grouping & period for decimal
|
||||
return accounting.formatNumber(normalizedNumber, precision, ',', '.');
|
||||
} else if (toFormat === '1') {
|
||||
// Period for grouping & comma for decimal
|
||||
return accounting.formatNumber(normalizedNumber, precision, '.', ',');
|
||||
} else if (toFormat === '2') {
|
||||
// Space for grouping & period for decimal
|
||||
return accounting.formatNumber(normalizedNumber, precision, ' ', '.');
|
||||
} else if (toFormat === '3') {
|
||||
// Space for grouping & comma for decimal
|
||||
return accounting.formatNumber(normalizedNumber, precision, ' ', ',');
|
||||
}
|
||||
};
|
||||
|
||||
export default formatNumber;
|
@@ -0,0 +1,23 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
import { add, divide, multiply, subtract } from 'lodash';
|
||||
|
||||
const mathOperation = ($: IGlobalVariable) => {
|
||||
const mathOperation = $.step.parameters.mathOperation as string;
|
||||
const values = ($.step.parameters.values as IJSONObject[]).map((value) =>
|
||||
Number(value.input)
|
||||
) as number[];
|
||||
|
||||
if (mathOperation === 'add') {
|
||||
return values.reduce((acc, curr) => add(acc, curr), 0);
|
||||
} else if (mathOperation === 'divide') {
|
||||
return values.reduce((acc, curr) => divide(acc, curr));
|
||||
} else if (mathOperation === 'makeNegative') {
|
||||
return values.map((value) => -value);
|
||||
} else if (mathOperation === 'multiply') {
|
||||
return values.reduce((acc, curr) => multiply(acc, curr), 1);
|
||||
} else if (mathOperation === 'subtract') {
|
||||
return values.reduce((acc, curr) => subtract(acc, curr));
|
||||
}
|
||||
};
|
||||
|
||||
export default mathOperation;
|
@@ -0,0 +1,15 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const randomNumber = ($: IGlobalVariable) => {
|
||||
const lowerRange = Number($.step.parameters.lowerRange);
|
||||
const upperRange = Number($.step.parameters.upperRange);
|
||||
const decimalPoints = Number($.step.parameters.decimalPoints) || 0;
|
||||
|
||||
return Number(
|
||||
(Math.random() * (upperRange - lowerRange) + lowerRange).toFixed(
|
||||
decimalPoints
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default randomNumber;
|
79
packages/backend/src/apps/formatter/actions/text/index.ts
Normal file
79
packages/backend/src/apps/formatter/actions/text/index.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
import capitalize from './transformers/capitalize';
|
||||
import extractEmailAddress from './transformers/extract-email-address';
|
||||
import extractNumber from './transformers/extract-number';
|
||||
import htmlToMarkdown from './transformers/html-to-markdown';
|
||||
import lowercase from './transformers/lowercase';
|
||||
import markdownToHtml from './transformers/markdown-to-html';
|
||||
import pluralize from './transformers/pluralize';
|
||||
import replace from './transformers/replace';
|
||||
import trimWhitespace from './transformers/trim-whitespace';
|
||||
import useDefaultValue from './transformers/use-default-value';
|
||||
|
||||
const transformers = {
|
||||
capitalize,
|
||||
extractEmailAddress,
|
||||
extractNumber,
|
||||
htmlToMarkdown,
|
||||
lowercase,
|
||||
markdownToHtml,
|
||||
pluralize,
|
||||
replace,
|
||||
trimWhitespace,
|
||||
useDefaultValue,
|
||||
};
|
||||
|
||||
export default defineAction({
|
||||
name: 'Text',
|
||||
key: 'text',
|
||||
description:
|
||||
'Transform text data to capitalize, extract emails, apply default value, and much more.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Transform',
|
||||
key: 'transform',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
options: [
|
||||
{ label: 'Capitalize', value: 'capitalize' },
|
||||
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
|
||||
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
|
||||
{ label: 'Extract Email Address', value: 'extractEmailAddress' },
|
||||
{ label: 'Extract Number', value: 'extractNumber' },
|
||||
{ label: 'Lowercase', value: 'lowercase' },
|
||||
{ label: 'Pluralize', value: 'pluralize' },
|
||||
{ label: 'Replace', value: 'replace' },
|
||||
{ label: 'Trim Whitespace', value: 'trimWhitespace' },
|
||||
{ label: 'Use Default Value', value: 'useDefaultValue' },
|
||||
],
|
||||
additionalFields: {
|
||||
type: 'query',
|
||||
name: 'getDynamicFields',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listTransformOptions',
|
||||
},
|
||||
{
|
||||
name: 'parameters.transform',
|
||||
value: '{parameters.transform}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const transformerName = $.step.parameters
|
||||
.transform as keyof typeof transformers;
|
||||
const output = transformers[transformerName]($);
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
output,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
@@ -0,0 +1,11 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import { capitalize as lodashCapitalize } from 'lodash';
|
||||
|
||||
const capitalize = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
const capitalizedInput = input.replace(/\w+/g, lodashCapitalize);
|
||||
|
||||
return capitalizedInput;
|
||||
};
|
||||
|
||||
export default capitalize;
|
@@ -0,0 +1,12 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const extractEmailAddress = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
const emailRegexp =
|
||||
/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
|
||||
|
||||
const email = input.match(emailRegexp);
|
||||
return email ? email[0] : '';
|
||||
};
|
||||
|
||||
export default extractEmailAddress;
|
@@ -0,0 +1,26 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const extractNumber = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
|
||||
// Example numbers that's supported:
|
||||
// 123
|
||||
// -123
|
||||
// 123456
|
||||
// -123456
|
||||
// 121,234
|
||||
// -121,234
|
||||
// 121.234
|
||||
// -121.234
|
||||
// 1,234,567.89
|
||||
// -1,234,567.89
|
||||
// 1.234.567,89
|
||||
// -1.234.567,89
|
||||
|
||||
const numberRegexp = /-?((\d{1,3})+\.?,?)+/g;
|
||||
|
||||
const numbers = input.match(numberRegexp);
|
||||
return numbers ? numbers[0] : '';
|
||||
};
|
||||
|
||||
export default extractNumber;
|
@@ -0,0 +1,11 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import { NodeHtmlMarkdown } from 'node-html-markdown';
|
||||
|
||||
const htmlToMarkdown = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
|
||||
const markdown = NodeHtmlMarkdown.translate(input);
|
||||
return markdown;
|
||||
};
|
||||
|
||||
export default htmlToMarkdown;
|
@@ -0,0 +1,8 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const lowercase = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
return input.toLowerCase();
|
||||
};
|
||||
|
||||
export default lowercase;
|
@@ -0,0 +1,13 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import showdown from 'showdown';
|
||||
|
||||
const converter = new showdown.Converter();
|
||||
|
||||
const markdownToHtml = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
|
||||
const html = converter.makeHtml(input);
|
||||
return html;
|
||||
};
|
||||
|
||||
export default markdownToHtml;
|
@@ -0,0 +1,9 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import pluralizeLibrary from 'pluralize';
|
||||
|
||||
const pluralize = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
return pluralizeLibrary(input);
|
||||
};
|
||||
|
||||
export default pluralize;
|
@@ -0,0 +1,12 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const replace = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
|
||||
const find = $.step.parameters.find as string;
|
||||
const replace = $.step.parameters.replace as string;
|
||||
|
||||
return input.replaceAll(find, replace);
|
||||
};
|
||||
|
||||
export default replace;
|
@@ -0,0 +1,8 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const trimWhitespace = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
return input.trim();
|
||||
};
|
||||
|
||||
export default trimWhitespace;
|
@@ -0,0 +1,13 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const useDefaultValue = ($: IGlobalVariable) => {
|
||||
const input = $.step.parameters.input as string;
|
||||
|
||||
if (input && input.trim().length > 0) {
|
||||
return input;
|
||||
}
|
||||
|
||||
return $.step.parameters.defaultValue as string;
|
||||
};
|
||||
|
||||
export default useDefaultValue;
|
@@ -0,0 +1,54 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
import findArrayItemByProperty from './transformers/find-array-item-by-property';
|
||||
|
||||
const transformers = {
|
||||
findArrayItemByProperty,
|
||||
};
|
||||
|
||||
export default defineAction({
|
||||
name: 'Utilities',
|
||||
key: 'utilities',
|
||||
description: 'Specific utilities to help you transform your data.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Transform',
|
||||
key: 'transform',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
options: [
|
||||
{
|
||||
label: 'Find Array Item By Property',
|
||||
value: 'findArrayItemByProperty',
|
||||
},
|
||||
],
|
||||
additionalFields: {
|
||||
type: 'query',
|
||||
name: 'getDynamicFields',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listTransformOptions',
|
||||
},
|
||||
{
|
||||
name: 'parameters.transform',
|
||||
value: '{parameters.transform}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const transformerName = $.step.parameters
|
||||
.transform as keyof typeof transformers;
|
||||
const output = transformers[transformerName]($);
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
output,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
@@ -0,0 +1,13 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import { find } from 'lodash';
|
||||
|
||||
const findArrayItemByProperty = ($: IGlobalVariable) => {
|
||||
const value = JSON.parse($.step.parameters.value as string);
|
||||
const propertyName = $.step.parameters.propertyName as string;
|
||||
const propertyValue = $.step.parameters.propertyValue as string;
|
||||
|
||||
const foundItem = find(value, { [propertyName]: propertyValue });
|
||||
return foundItem;
|
||||
};
|
||||
|
||||
export default findArrayItemByProperty;
|
3
packages/backend/src/apps/formatter/assets/favicon.svg
Normal file
3
packages/backend/src/apps/formatter/assets/favicon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 4H20M4 12H20M4 20H20M4 8H14M4 16H14" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 243 B |
@@ -0,0 +1,3 @@
|
||||
import listTransformOptions from './list-transform-options';
|
||||
|
||||
export default [listTransformOptions];
|
@@ -0,0 +1,51 @@
|
||||
import formatOptions from './options/format';
|
||||
import timezoneOptions from './options/timezone';
|
||||
|
||||
const formatDateTime = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The datetime you want to format.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'From Format',
|
||||
key: 'fromFormat',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'The format of the input.',
|
||||
variables: true,
|
||||
options: formatOptions,
|
||||
},
|
||||
{
|
||||
label: 'From Timezone',
|
||||
key: 'fromTimezone',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'The timezone of the input.',
|
||||
variables: true,
|
||||
options: timezoneOptions,
|
||||
},
|
||||
{
|
||||
label: 'To Format',
|
||||
key: 'toFormat',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'The format of the output.',
|
||||
variables: true,
|
||||
options: formatOptions,
|
||||
},
|
||||
{
|
||||
label: 'To Timezone',
|
||||
key: 'toTimezone',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'The timezone of the output.',
|
||||
variables: true,
|
||||
options: timezoneOptions,
|
||||
},
|
||||
];
|
||||
|
||||
export default formatDateTime;
|
@@ -0,0 +1,64 @@
|
||||
const formatOptions = [
|
||||
{
|
||||
label: 'ccc MMM dd HH:mm:ssZZZ yyyy (Wed Aug 23 12:25:36-0000 2023)',
|
||||
value: 'ccc MMM dd HH:mm:ssZZZ yyyy',
|
||||
},
|
||||
{
|
||||
label: 'MMMM dd yyyy HH:mm:ss (August 23 2023 12:25:36)',
|
||||
value: 'MMMM dd yyyy HH:mm:ss',
|
||||
},
|
||||
{
|
||||
label: 'MMMM dd yyyy (August 23 2023)',
|
||||
value: 'MMMM dd yyyy',
|
||||
},
|
||||
{
|
||||
label: 'MMM dd yyyy (Aug 23 2023)',
|
||||
value: 'MMM dd yyyy',
|
||||
},
|
||||
{
|
||||
label: 'yyyy-MM-dd HH:mm:ss ZZZ (2023-08-23 12:25:36 -0000)',
|
||||
value: 'yyyy-MM-dd HH:mm:ss ZZZ',
|
||||
},
|
||||
{
|
||||
label: 'yyyy-MM-dd (2023-08-23)',
|
||||
value: 'yyyy-MM-dd',
|
||||
},
|
||||
{
|
||||
label: 'MM-dd-yyyy (08-23-2023)',
|
||||
value: 'MM-dd-yyyy',
|
||||
},
|
||||
{
|
||||
label: 'MM/dd/yyyy (08/23/2023)',
|
||||
value: 'MM/dd/yyyy',
|
||||
},
|
||||
{
|
||||
label: 'MM/dd/yy (08/23/23)',
|
||||
value: 'MM/dd/yy',
|
||||
},
|
||||
{
|
||||
label: 'dd-MM-yyyy (23-08-2023)',
|
||||
value: 'dd-MM-yyyy',
|
||||
},
|
||||
{
|
||||
label: 'dd/MM/yyyy (23/08/2023)',
|
||||
value: 'dd/MM/yyyy',
|
||||
},
|
||||
{
|
||||
label: 'dd/MM/yy (23/08/23)',
|
||||
value: 'dd/MM/yy',
|
||||
},
|
||||
{
|
||||
label: 'MM-yyyy (08-2023)',
|
||||
value: 'MM-yyyy',
|
||||
},
|
||||
{
|
||||
label: 'Unix timestamp in seconds (1694008283)',
|
||||
value: 'X',
|
||||
},
|
||||
{
|
||||
label: 'Unix timestamp in milliseconds (1694008306315)',
|
||||
value: 'x',
|
||||
},
|
||||
];
|
||||
|
||||
export default formatOptions;
|
@@ -0,0 +1,449 @@
|
||||
// The list from Intl.supportedValuesOf('timeZone') which is used by Luxon.
|
||||
|
||||
const timezoneOptions = [
|
||||
{ label: 'Africa/Abidjan', value: 'Africa/Abidjan' },
|
||||
{ label: 'Africa/Accra', value: 'Africa/Accra' },
|
||||
{ label: 'Africa/Addis_Ababa', value: 'Africa/Addis_Ababa' },
|
||||
{ label: 'Africa/Algiers', value: 'Africa/Algiers' },
|
||||
{ label: 'Africa/Asmera', value: 'Africa/Asmera' },
|
||||
{ label: 'Africa/Bamako', value: 'Africa/Bamako' },
|
||||
{ label: 'Africa/Bangui', value: 'Africa/Bangui' },
|
||||
{ label: 'Africa/Banjul', value: 'Africa/Banjul' },
|
||||
{ label: 'Africa/Bissau', value: 'Africa/Bissau' },
|
||||
{ label: 'Africa/Blantyre', value: 'Africa/Blantyre' },
|
||||
{ label: 'Africa/Brazzaville', value: 'Africa/Brazzaville' },
|
||||
{ label: 'Africa/Bujumbura', value: 'Africa/Bujumbura' },
|
||||
{ label: 'Africa/Cairo', value: 'Africa/Cairo' },
|
||||
{ label: 'Africa/Casablanca', value: 'Africa/Casablanca' },
|
||||
{ label: 'Africa/Ceuta', value: 'Africa/Ceuta' },
|
||||
{ label: 'Africa/Conakry', value: 'Africa/Conakry' },
|
||||
{ label: 'Africa/Dakar', value: 'Africa/Dakar' },
|
||||
{ label: 'Africa/Dar_es_Salaam', value: 'Africa/Dar_es_Salaam' },
|
||||
{ label: 'Africa/Djibouti', value: 'Africa/Djibouti' },
|
||||
{ label: 'Africa/Douala', value: 'Africa/Douala' },
|
||||
{ label: 'Africa/El_Aaiun', value: 'Africa/El_Aaiun' },
|
||||
{ label: 'Africa/Freetown', value: 'Africa/Freetown' },
|
||||
{ label: 'Africa/Gaborone', value: 'Africa/Gaborone' },
|
||||
{ label: 'Africa/Harare', value: 'Africa/Harare' },
|
||||
{ label: 'Africa/Johannesburg', value: 'Africa/Johannesburg' },
|
||||
{ label: 'Africa/Juba', value: 'Africa/Juba' },
|
||||
{ label: 'Africa/Kampala', value: 'Africa/Kampala' },
|
||||
{ label: 'Africa/Khartoum', value: 'Africa/Khartoum' },
|
||||
{ label: 'Africa/Kigali', value: 'Africa/Kigali' },
|
||||
{ label: 'Africa/Kinshasa', value: 'Africa/Kinshasa' },
|
||||
{ label: 'Africa/Lagos', value: 'Africa/Lagos' },
|
||||
{ label: 'Africa/Libreville', value: 'Africa/Libreville' },
|
||||
{ label: 'Africa/Lome', value: 'Africa/Lome' },
|
||||
{ label: 'Africa/Luanda', value: 'Africa/Luanda' },
|
||||
{ label: 'Africa/Lubumbashi', value: 'Africa/Lubumbashi' },
|
||||
{ label: 'Africa/Lusaka', value: 'Africa/Lusaka' },
|
||||
{ label: 'Africa/Malabo', value: 'Africa/Malabo' },
|
||||
{ label: 'Africa/Maputo', value: 'Africa/Maputo' },
|
||||
{ label: 'Africa/Maseru', value: 'Africa/Maseru' },
|
||||
{ label: 'Africa/Mbabane', value: 'Africa/Mbabane' },
|
||||
{ label: 'Africa/Mogadishu', value: 'Africa/Mogadishu' },
|
||||
{ label: 'Africa/Monrovia', value: 'Africa/Monrovia' },
|
||||
{ label: 'Africa/Nairobi', value: 'Africa/Nairobi' },
|
||||
{ label: 'Africa/Ndjamena', value: 'Africa/Ndjamena' },
|
||||
{ label: 'Africa/Niamey', value: 'Africa/Niamey' },
|
||||
{ label: 'Africa/Nouakchott', value: 'Africa/Nouakchott' },
|
||||
{ label: 'Africa/Ouagadougou', value: 'Africa/Ouagadougou' },
|
||||
{ label: 'Africa/Porto-Novo', value: 'Africa/Porto-Novo' },
|
||||
{ label: 'Africa/Sao_Tome', value: 'Africa/Sao_Tome' },
|
||||
{ label: 'Africa/Tripoli', value: 'Africa/Tripoli' },
|
||||
{ label: 'Africa/Tunis', value: 'Africa/Tunis' },
|
||||
{ label: 'Africa/Windhoek', value: 'Africa/Windhoek' },
|
||||
{ label: 'America/Adak', value: 'America/Adak' },
|
||||
{ label: 'America/Anchorage', value: 'America/Anchorage' },
|
||||
{ label: 'America/Anguilla', value: 'America/Anguilla' },
|
||||
{ label: 'America/Antigua', value: 'America/Antigua' },
|
||||
{ label: 'America/Araguaina', value: 'America/Araguaina' },
|
||||
{ label: 'America/Argentina/La_Rioja', value: 'America/Argentina/La_Rioja' },
|
||||
{
|
||||
label: 'America/Argentina/Rio_Gallegos',
|
||||
value: 'America/Argentina/Rio_Gallegos',
|
||||
},
|
||||
{ label: 'America/Argentina/Salta', value: 'America/Argentina/Salta' },
|
||||
{ label: 'America/Argentina/San_Juan', value: 'America/Argentina/San_Juan' },
|
||||
{ label: 'America/Argentina/San_Luis', value: 'America/Argentina/San_Luis' },
|
||||
{ label: 'America/Argentina/Tucuman', value: 'America/Argentina/Tucuman' },
|
||||
{ label: 'America/Argentina/Ushuaia', value: 'America/Argentina/Ushuaia' },
|
||||
{ label: 'America/Aruba', value: 'America/Aruba' },
|
||||
{ label: 'America/Asuncion', value: 'America/Asuncion' },
|
||||
{ label: 'America/Bahia', value: 'America/Bahia' },
|
||||
{ label: 'America/Bahia_Banderas', value: 'America/Bahia_Banderas' },
|
||||
{ label: 'America/Barbados', value: 'America/Barbados' },
|
||||
{ label: 'America/Belem', value: 'America/Belem' },
|
||||
{ label: 'America/Belize', value: 'America/Belize' },
|
||||
{ label: 'America/Blanc-Sablon', value: 'America/Blanc-Sablon' },
|
||||
{ label: 'America/Boa_Vista', value: 'America/Boa_Vista' },
|
||||
{ label: 'America/Bogota', value: 'America/Bogota' },
|
||||
{ label: 'America/Boise', value: 'America/Boise' },
|
||||
{ label: 'America/Buenos_Aires', value: 'America/Buenos_Aires' },
|
||||
{ label: 'America/Cambridge_Bay', value: 'America/Cambridge_Bay' },
|
||||
{ label: 'America/Campo_Grande', value: 'America/Campo_Grande' },
|
||||
{ label: 'America/Cancun', value: 'America/Cancun' },
|
||||
{ label: 'America/Caracas', value: 'America/Caracas' },
|
||||
{ label: 'America/Catamarca', value: 'America/Catamarca' },
|
||||
{ label: 'America/Cayenne', value: 'America/Cayenne' },
|
||||
{ label: 'America/Cayman', value: 'America/Cayman' },
|
||||
{ label: 'America/Chicago', value: 'America/Chicago' },
|
||||
{ label: 'America/Chihuahua', value: 'America/Chihuahua' },
|
||||
{ label: 'America/Ciudad_Juarez', value: 'America/Ciudad_Juarez' },
|
||||
{ label: 'America/Coral_Harbour', value: 'America/Coral_Harbour' },
|
||||
{ label: 'America/Cordoba', value: 'America/Cordoba' },
|
||||
{ label: 'America/Costa_Rica', value: 'America/Costa_Rica' },
|
||||
{ label: 'America/Creston', value: 'America/Creston' },
|
||||
{ label: 'America/Cuiaba', value: 'America/Cuiaba' },
|
||||
{ label: 'America/Curacao', value: 'America/Curacao' },
|
||||
{ label: 'America/Danmarkshavn', value: 'America/Danmarkshavn' },
|
||||
{ label: 'America/Dawson', value: 'America/Dawson' },
|
||||
{ label: 'America/Dawson_Creek', value: 'America/Dawson_Creek' },
|
||||
{ label: 'America/Denver', value: 'America/Denver' },
|
||||
{ label: 'America/Detroit', value: 'America/Detroit' },
|
||||
{ label: 'America/Dominica', value: 'America/Dominica' },
|
||||
{ label: 'America/Edmonton', value: 'America/Edmonton' },
|
||||
{ label: 'America/Eirunepe', value: 'America/Eirunepe' },
|
||||
{ label: 'America/El_Salvador', value: 'America/El_Salvador' },
|
||||
{ label: 'America/Fort_Nelson', value: 'America/Fort_Nelson' },
|
||||
{ label: 'America/Fortaleza', value: 'America/Fortaleza' },
|
||||
{ label: 'America/Glace_Bay', value: 'America/Glace_Bay' },
|
||||
{ label: 'America/Godthab', value: 'America/Godthab' },
|
||||
{ label: 'America/Goose_Bay', value: 'America/Goose_Bay' },
|
||||
{ label: 'America/Grand_Turk', value: 'America/Grand_Turk' },
|
||||
{ label: 'America/Grenada', value: 'America/Grenada' },
|
||||
{ label: 'America/Guadeloupe', value: 'America/Guadeloupe' },
|
||||
{ label: 'America/Guatemala', value: 'America/Guatemala' },
|
||||
{ label: 'America/Guayaquil', value: 'America/Guayaquil' },
|
||||
{ label: 'America/Guyana', value: 'America/Guyana' },
|
||||
{ label: 'America/Halifax', value: 'America/Halifax' },
|
||||
{ label: 'America/Havana', value: 'America/Havana' },
|
||||
{ label: 'America/Hermosillo', value: 'America/Hermosillo' },
|
||||
{ label: 'America/Indiana/Knox', value: 'America/Indiana/Knox' },
|
||||
{ label: 'America/Indiana/Marengo', value: 'America/Indiana/Marengo' },
|
||||
{ label: 'America/Indiana/Petersburg', value: 'America/Indiana/Petersburg' },
|
||||
{ label: 'America/Indiana/Tell_City', value: 'America/Indiana/Tell_City' },
|
||||
{ label: 'America/Indiana/Vevay', value: 'America/Indiana/Vevay' },
|
||||
{ label: 'America/Indiana/Vincennes', value: 'America/Indiana/Vincennes' },
|
||||
{ label: 'America/Indiana/Winamac', value: 'America/Indiana/Winamac' },
|
||||
{ label: 'America/Indianapolis', value: 'America/Indianapolis' },
|
||||
{ label: 'America/Inuvik', value: 'America/Inuvik' },
|
||||
{ label: 'America/Iqaluit', value: 'America/Iqaluit' },
|
||||
{ label: 'America/Jamaica', value: 'America/Jamaica' },
|
||||
{ label: 'America/Jujuy', value: 'America/Jujuy' },
|
||||
{ label: 'America/Juneau', value: 'America/Juneau' },
|
||||
{
|
||||
label: 'America/Kentucky/Monticello',
|
||||
value: 'America/Kentucky/Monticello',
|
||||
},
|
||||
{ label: 'America/Kralendijk', value: 'America/Kralendijk' },
|
||||
{ label: 'America/La_Paz', value: 'America/La_Paz' },
|
||||
{ label: 'America/Lima', value: 'America/Lima' },
|
||||
{ label: 'America/Los_Angeles', value: 'America/Los_Angeles' },
|
||||
{ label: 'America/Louisville', value: 'America/Louisville' },
|
||||
{ label: 'America/Lower_Princes', value: 'America/Lower_Princes' },
|
||||
{ label: 'America/Maceio', value: 'America/Maceio' },
|
||||
{ label: 'America/Managua', value: 'America/Managua' },
|
||||
{ label: 'America/Manaus', value: 'America/Manaus' },
|
||||
{ label: 'America/Marigot', value: 'America/Marigot' },
|
||||
{ label: 'America/Martinique', value: 'America/Martinique' },
|
||||
{ label: 'America/Matamoros', value: 'America/Matamoros' },
|
||||
{ label: 'America/Mazatlan', value: 'America/Mazatlan' },
|
||||
{ label: 'America/Mendoza', value: 'America/Mendoza' },
|
||||
{ label: 'America/Menominee', value: 'America/Menominee' },
|
||||
{ label: 'America/Merida', value: 'America/Merida' },
|
||||
{ label: 'America/Metlakatla', value: 'America/Metlakatla' },
|
||||
{ label: 'America/Mexico_City', value: 'America/Mexico_City' },
|
||||
{ label: 'America/Miquelon', value: 'America/Miquelon' },
|
||||
{ label: 'America/Moncton', value: 'America/Moncton' },
|
||||
{ label: 'America/Monterrey', value: 'America/Monterrey' },
|
||||
{ label: 'America/Montevideo', value: 'America/Montevideo' },
|
||||
{ label: 'America/Montserrat', value: 'America/Montserrat' },
|
||||
{ label: 'America/Nassau', value: 'America/Nassau' },
|
||||
{ label: 'America/New_York', value: 'America/New_York' },
|
||||
{ label: 'America/Nipigon', value: 'America/Nipigon' },
|
||||
{ label: 'America/Nome', value: 'America/Nome' },
|
||||
{ label: 'America/Noronha', value: 'America/Noronha' },
|
||||
{
|
||||
label: 'America/North_Dakota/Beulah',
|
||||
value: 'America/North_Dakota/Beulah',
|
||||
},
|
||||
{
|
||||
label: 'America/North_Dakota/Center',
|
||||
value: 'America/North_Dakota/Center',
|
||||
},
|
||||
{
|
||||
label: 'America/North_Dakota/New_Salem',
|
||||
value: 'America/North_Dakota/New_Salem',
|
||||
},
|
||||
{ label: 'America/Ojinaga', value: 'America/Ojinaga' },
|
||||
{ label: 'America/Panama', value: 'America/Panama' },
|
||||
{ label: 'America/Pangnirtung', value: 'America/Pangnirtung' },
|
||||
{ label: 'America/Paramaribo', value: 'America/Paramaribo' },
|
||||
{ label: 'America/Phoenix', value: 'America/Phoenix' },
|
||||
{ label: 'America/Port-au-Prince', value: 'America/Port-au-Prince' },
|
||||
{ label: 'America/Port_of_Spain', value: 'America/Port_of_Spain' },
|
||||
{ label: 'America/Porto_Velho', value: 'America/Porto_Velho' },
|
||||
{ label: 'America/Puerto_Rico', value: 'America/Puerto_Rico' },
|
||||
{ label: 'America/Punta_Arenas', value: 'America/Punta_Arenas' },
|
||||
{ label: 'America/Rainy_River', value: 'America/Rainy_River' },
|
||||
{ label: 'America/Rankin_Inlet', value: 'America/Rankin_Inlet' },
|
||||
{ label: 'America/Recife', value: 'America/Recife' },
|
||||
{ label: 'America/Regina', value: 'America/Regina' },
|
||||
{ label: 'America/Resolute', value: 'America/Resolute' },
|
||||
{ label: 'America/Rio_Branco', value: 'America/Rio_Branco' },
|
||||
{ label: 'America/Santa_Isabel', value: 'America/Santa_Isabel' },
|
||||
{ label: 'America/Santarem', value: 'America/Santarem' },
|
||||
{ label: 'America/Santiago', value: 'America/Santiago' },
|
||||
{ label: 'America/Santo_Domingo', value: 'America/Santo_Domingo' },
|
||||
{ label: 'America/Sao_Paulo', value: 'America/Sao_Paulo' },
|
||||
{ label: 'America/Scoresbysund', value: 'America/Scoresbysund' },
|
||||
{ label: 'America/Sitka', value: 'America/Sitka' },
|
||||
{ label: 'America/St_Barthelemy', value: 'America/St_Barthelemy' },
|
||||
{ label: 'America/St_Johns', value: 'America/St_Johns' },
|
||||
{ label: 'America/St_Kitts', value: 'America/St_Kitts' },
|
||||
{ label: 'America/St_Lucia', value: 'America/St_Lucia' },
|
||||
{ label: 'America/St_Thomas', value: 'America/St_Thomas' },
|
||||
{ label: 'America/St_Vincent', value: 'America/St_Vincent' },
|
||||
{ label: 'America/Swift_Current', value: 'America/Swift_Current' },
|
||||
{ label: 'America/Tegucigalpa', value: 'America/Tegucigalpa' },
|
||||
{ label: 'America/Thule', value: 'America/Thule' },
|
||||
{ label: 'America/Thunder_Bay', value: 'America/Thunder_Bay' },
|
||||
{ label: 'America/Tijuana', value: 'America/Tijuana' },
|
||||
{ label: 'America/Toronto', value: 'America/Toronto' },
|
||||
{ label: 'America/Tortola', value: 'America/Tortola' },
|
||||
{ label: 'America/Vancouver', value: 'America/Vancouver' },
|
||||
{ label: 'America/Whitehorse', value: 'America/Whitehorse' },
|
||||
{ label: 'America/Winnipeg', value: 'America/Winnipeg' },
|
||||
{ label: 'America/Yakutat', value: 'America/Yakutat' },
|
||||
{ label: 'America/Yellowknife', value: 'America/Yellowknife' },
|
||||
{ label: 'Antarctica/Casey', value: 'Antarctica/Casey' },
|
||||
{ label: 'Antarctica/Davis', value: 'Antarctica/Davis' },
|
||||
{ label: 'Antarctica/DumontDUrville', value: 'Antarctica/DumontDUrville' },
|
||||
{ label: 'Antarctica/Macquarie', value: 'Antarctica/Macquarie' },
|
||||
{ label: 'Antarctica/Mawson', value: 'Antarctica/Mawson' },
|
||||
{ label: 'Antarctica/McMurdo', value: 'Antarctica/McMurdo' },
|
||||
{ label: 'Antarctica/Palmer', value: 'Antarctica/Palmer' },
|
||||
{ label: 'Antarctica/Rothera', value: 'Antarctica/Rothera' },
|
||||
{ label: 'Antarctica/Syowa', value: 'Antarctica/Syowa' },
|
||||
{ label: 'Antarctica/Troll', value: 'Antarctica/Troll' },
|
||||
{ label: 'Antarctica/Vostok', value: 'Antarctica/Vostok' },
|
||||
{ label: 'Arctic/Longyearbyen', value: 'Arctic/Longyearbyen' },
|
||||
{ label: 'Asia/Aden', value: 'Asia/Aden' },
|
||||
{ label: 'Asia/Almaty', value: 'Asia/Almaty' },
|
||||
{ label: 'Asia/Amman', value: 'Asia/Amman' },
|
||||
{ label: 'Asia/Anadyr', value: 'Asia/Anadyr' },
|
||||
{ label: 'Asia/Aqtau', value: 'Asia/Aqtau' },
|
||||
{ label: 'Asia/Aqtobe', value: 'Asia/Aqtobe' },
|
||||
{ label: 'Asia/Ashgabat', value: 'Asia/Ashgabat' },
|
||||
{ label: 'Asia/Atyrau', value: 'Asia/Atyrau' },
|
||||
{ label: 'Asia/Baghdad', value: 'Asia/Baghdad' },
|
||||
{ label: 'Asia/Bahrain', value: 'Asia/Bahrain' },
|
||||
{ label: 'Asia/Baku', value: 'Asia/Baku' },
|
||||
{ label: 'Asia/Bangkok', value: 'Asia/Bangkok' },
|
||||
{ label: 'Asia/Barnaul', value: 'Asia/Barnaul' },
|
||||
{ label: 'Asia/Beirut', value: 'Asia/Beirut' },
|
||||
{ label: 'Asia/Bishkek', value: 'Asia/Bishkek' },
|
||||
{ label: 'Asia/Brunei', value: 'Asia/Brunei' },
|
||||
{ label: 'Asia/Calcutta', value: 'Asia/Calcutta' },
|
||||
{ label: 'Asia/Chita', value: 'Asia/Chita' },
|
||||
{ label: 'Asia/Choibalsan', value: 'Asia/Choibalsan' },
|
||||
{ label: 'Asia/Colombo', value: 'Asia/Colombo' },
|
||||
{ label: 'Asia/Damascus', value: 'Asia/Damascus' },
|
||||
{ label: 'Asia/Dhaka', value: 'Asia/Dhaka' },
|
||||
{ label: 'Asia/Dili', value: 'Asia/Dili' },
|
||||
{ label: 'Asia/Dubai', value: 'Asia/Dubai' },
|
||||
{ label: 'Asia/Dushanbe', value: 'Asia/Dushanbe' },
|
||||
{ label: 'Asia/Famagusta', value: 'Asia/Famagusta' },
|
||||
{ label: 'Asia/Gaza', value: 'Asia/Gaza' },
|
||||
{ label: 'Asia/Hebron', value: 'Asia/Hebron' },
|
||||
{ label: 'Asia/Hong_Kong', value: 'Asia/Hong_Kong' },
|
||||
{ label: 'Asia/Hovd', value: 'Asia/Hovd' },
|
||||
{ label: 'Asia/Irkutsk', value: 'Asia/Irkutsk' },
|
||||
{ label: 'Asia/Jakarta', value: 'Asia/Jakarta' },
|
||||
{ label: 'Asia/Jayapura', value: 'Asia/Jayapura' },
|
||||
{ label: 'Asia/Jerusalem', value: 'Asia/Jerusalem' },
|
||||
{ label: 'Asia/Kabul', value: 'Asia/Kabul' },
|
||||
{ label: 'Asia/Kamchatka', value: 'Asia/Kamchatka' },
|
||||
{ label: 'Asia/Karachi', value: 'Asia/Karachi' },
|
||||
{ label: 'Asia/Katmandu', value: 'Asia/Katmandu' },
|
||||
{ label: 'Asia/Khandyga', value: 'Asia/Khandyga' },
|
||||
{ label: 'Asia/Krasnoyarsk', value: 'Asia/Krasnoyarsk' },
|
||||
{ label: 'Asia/Kuala_Lumpur', value: 'Asia/Kuala_Lumpur' },
|
||||
{ label: 'Asia/Kuching', value: 'Asia/Kuching' },
|
||||
{ label: 'Asia/Kuwait', value: 'Asia/Kuwait' },
|
||||
{ label: 'Asia/Macau', value: 'Asia/Macau' },
|
||||
{ label: 'Asia/Magadan', value: 'Asia/Magadan' },
|
||||
{ label: 'Asia/Makassar', value: 'Asia/Makassar' },
|
||||
{ label: 'Asia/Manila', value: 'Asia/Manila' },
|
||||
{ label: 'Asia/Muscat', value: 'Asia/Muscat' },
|
||||
{ label: 'Asia/Nicosia', value: 'Asia/Nicosia' },
|
||||
{ label: 'Asia/Novokuznetsk', value: 'Asia/Novokuznetsk' },
|
||||
{ label: 'Asia/Novosibirsk', value: 'Asia/Novosibirsk' },
|
||||
{ label: 'Asia/Omsk', value: 'Asia/Omsk' },
|
||||
{ label: 'Asia/Oral', value: 'Asia/Oral' },
|
||||
{ label: 'Asia/Phnom_Penh', value: 'Asia/Phnom_Penh' },
|
||||
{ label: 'Asia/Pontianak', value: 'Asia/Pontianak' },
|
||||
{ label: 'Asia/Pyongyang', value: 'Asia/Pyongyang' },
|
||||
{ label: 'Asia/Qatar', value: 'Asia/Qatar' },
|
||||
{ label: 'Asia/Qostanay', value: 'Asia/Qostanay' },
|
||||
{ label: 'Asia/Qyzylorda', value: 'Asia/Qyzylorda' },
|
||||
{ label: 'Asia/Rangoon', value: 'Asia/Rangoon' },
|
||||
{ label: 'Asia/Riyadh', value: 'Asia/Riyadh' },
|
||||
{ label: 'Asia/Saigon', value: 'Asia/Saigon' },
|
||||
{ label: 'Asia/Sakhalin', value: 'Asia/Sakhalin' },
|
||||
{ label: 'Asia/Samarkand', value: 'Asia/Samarkand' },
|
||||
{ label: 'Asia/Seoul', value: 'Asia/Seoul' },
|
||||
{ label: 'Asia/Shanghai', value: 'Asia/Shanghai' },
|
||||
{ label: 'Asia/Singapore', value: 'Asia/Singapore' },
|
||||
{ label: 'Asia/Srednekolymsk', value: 'Asia/Srednekolymsk' },
|
||||
{ label: 'Asia/Taipei', value: 'Asia/Taipei' },
|
||||
{ label: 'Asia/Tashkent', value: 'Asia/Tashkent' },
|
||||
{ label: 'Asia/Tbilisi', value: 'Asia/Tbilisi' },
|
||||
{ label: 'Asia/Tehran', value: 'Asia/Tehran' },
|
||||
{ label: 'Asia/Thimphu', value: 'Asia/Thimphu' },
|
||||
{ label: 'Asia/Tokyo', value: 'Asia/Tokyo' },
|
||||
{ label: 'Asia/Tomsk', value: 'Asia/Tomsk' },
|
||||
{ label: 'Asia/Ulaanbaatar', value: 'Asia/Ulaanbaatar' },
|
||||
{ label: 'Asia/Urumqi', value: 'Asia/Urumqi' },
|
||||
{ label: 'Asia/Ust-Nera', value: 'Asia/Ust-Nera' },
|
||||
{ label: 'Asia/Vientiane', value: 'Asia/Vientiane' },
|
||||
{ label: 'Asia/Vladivostok', value: 'Asia/Vladivostok' },
|
||||
{ label: 'Asia/Yakutsk', value: 'Asia/Yakutsk' },
|
||||
{ label: 'Asia/Yekaterinburg', value: 'Asia/Yekaterinburg' },
|
||||
{ label: 'Asia/Yerevan', value: 'Asia/Yerevan' },
|
||||
{ label: 'Atlantic/Azores', value: 'Atlantic/Azores' },
|
||||
{ label: 'Atlantic/Bermuda', value: 'Atlantic/Bermuda' },
|
||||
{ label: 'Atlantic/Canary', value: 'Atlantic/Canary' },
|
||||
{ label: 'Atlantic/Cape_Verde', value: 'Atlantic/Cape_Verde' },
|
||||
{ label: 'Atlantic/Faeroe', value: 'Atlantic/Faeroe' },
|
||||
{ label: 'Atlantic/Madeira', value: 'Atlantic/Madeira' },
|
||||
{ label: 'Atlantic/Reykjavik', value: 'Atlantic/Reykjavik' },
|
||||
{ label: 'Atlantic/South_Georgia', value: 'Atlantic/South_Georgia' },
|
||||
{ label: 'Atlantic/St_Helena', value: 'Atlantic/St_Helena' },
|
||||
{ label: 'Atlantic/Stanley', value: 'Atlantic/Stanley' },
|
||||
{ label: 'Australia/Adelaide', value: 'Australia/Adelaide' },
|
||||
{ label: 'Australia/Brisbane', value: 'Australia/Brisbane' },
|
||||
{ label: 'Australia/Broken_Hill', value: 'Australia/Broken_Hill' },
|
||||
{ label: 'Australia/Currie', value: 'Australia/Currie' },
|
||||
{ label: 'Australia/Darwin', value: 'Australia/Darwin' },
|
||||
{ label: 'Australia/Eucla', value: 'Australia/Eucla' },
|
||||
{ label: 'Australia/Hobart', value: 'Australia/Hobart' },
|
||||
{ label: 'Australia/Lindeman', value: 'Australia/Lindeman' },
|
||||
{ label: 'Australia/Lord_Howe', value: 'Australia/Lord_Howe' },
|
||||
{ label: 'Australia/Melbourne', value: 'Australia/Melbourne' },
|
||||
{ label: 'Australia/Perth', value: 'Australia/Perth' },
|
||||
{ label: 'Australia/Sydney', value: 'Australia/Sydney' },
|
||||
{ label: 'Europe/Amsterdam', value: 'Europe/Amsterdam' },
|
||||
{ label: 'Europe/Andorra', value: 'Europe/Andorra' },
|
||||
{ label: 'Europe/Astrakhan', value: 'Europe/Astrakhan' },
|
||||
{ label: 'Europe/Athens', value: 'Europe/Athens' },
|
||||
{ label: 'Europe/Belgrade', value: 'Europe/Belgrade' },
|
||||
{ label: 'Europe/Berlin', value: 'Europe/Berlin' },
|
||||
{ label: 'Europe/Bratislava', value: 'Europe/Bratislava' },
|
||||
{ label: 'Europe/Brussels', value: 'Europe/Brussels' },
|
||||
{ label: 'Europe/Bucharest', value: 'Europe/Bucharest' },
|
||||
{ label: 'Europe/Budapest', value: 'Europe/Budapest' },
|
||||
{ label: 'Europe/Busingen', value: 'Europe/Busingen' },
|
||||
{ label: 'Europe/Chisinau', value: 'Europe/Chisinau' },
|
||||
{ label: 'Europe/Copenhagen', value: 'Europe/Copenhagen' },
|
||||
{ label: 'Europe/Dublin', value: 'Europe/Dublin' },
|
||||
{ label: 'Europe/Gibraltar', value: 'Europe/Gibraltar' },
|
||||
{ label: 'Europe/Guernsey', value: 'Europe/Guernsey' },
|
||||
{ label: 'Europe/Helsinki', value: 'Europe/Helsinki' },
|
||||
{ label: 'Europe/Isle_of_Man', value: 'Europe/Isle_of_Man' },
|
||||
{ label: 'Europe/Istanbul', value: 'Europe/Istanbul' },
|
||||
{ label: 'Europe/Jersey', value: 'Europe/Jersey' },
|
||||
{ label: 'Europe/Kaliningrad', value: 'Europe/Kaliningrad' },
|
||||
{ label: 'Europe/Kiev', value: 'Europe/Kiev' },
|
||||
{ label: 'Europe/Kirov', value: 'Europe/Kirov' },
|
||||
{ label: 'Europe/Lisbon', value: 'Europe/Lisbon' },
|
||||
{ label: 'Europe/Ljubljana', value: 'Europe/Ljubljana' },
|
||||
{ label: 'Europe/London', value: 'Europe/London' },
|
||||
{ label: 'Europe/Luxembourg', value: 'Europe/Luxembourg' },
|
||||
{ label: 'Europe/Madrid', value: 'Europe/Madrid' },
|
||||
{ label: 'Europe/Malta', value: 'Europe/Malta' },
|
||||
{ label: 'Europe/Mariehamn', value: 'Europe/Mariehamn' },
|
||||
{ label: 'Europe/Minsk', value: 'Europe/Minsk' },
|
||||
{ label: 'Europe/Monaco', value: 'Europe/Monaco' },
|
||||
{ label: 'Europe/Moscow', value: 'Europe/Moscow' },
|
||||
{ label: 'Europe/Oslo', value: 'Europe/Oslo' },
|
||||
{ label: 'Europe/Paris', value: 'Europe/Paris' },
|
||||
{ label: 'Europe/Podgorica', value: 'Europe/Podgorica' },
|
||||
{ label: 'Europe/Prague', value: 'Europe/Prague' },
|
||||
{ label: 'Europe/Riga', value: 'Europe/Riga' },
|
||||
{ label: 'Europe/Rome', value: 'Europe/Rome' },
|
||||
{ label: 'Europe/Samara', value: 'Europe/Samara' },
|
||||
{ label: 'Europe/San_Marino', value: 'Europe/San_Marino' },
|
||||
{ label: 'Europe/Sarajevo', value: 'Europe/Sarajevo' },
|
||||
{ label: 'Europe/Saratov', value: 'Europe/Saratov' },
|
||||
{ label: 'Europe/Simferopol', value: 'Europe/Simferopol' },
|
||||
{ label: 'Europe/Skopje', value: 'Europe/Skopje' },
|
||||
{ label: 'Europe/Sofia', value: 'Europe/Sofia' },
|
||||
{ label: 'Europe/Stockholm', value: 'Europe/Stockholm' },
|
||||
{ label: 'Europe/Tallinn', value: 'Europe/Tallinn' },
|
||||
{ label: 'Europe/Tirane', value: 'Europe/Tirane' },
|
||||
{ label: 'Europe/Ulyanovsk', value: 'Europe/Ulyanovsk' },
|
||||
{ label: 'Europe/Uzhgorod', value: 'Europe/Uzhgorod' },
|
||||
{ label: 'Europe/Vaduz', value: 'Europe/Vaduz' },
|
||||
{ label: 'Europe/Vatican', value: 'Europe/Vatican' },
|
||||
{ label: 'Europe/Vienna', value: 'Europe/Vienna' },
|
||||
{ label: 'Europe/Vilnius', value: 'Europe/Vilnius' },
|
||||
{ label: 'Europe/Volgograd', value: 'Europe/Volgograd' },
|
||||
{ label: 'Europe/Warsaw', value: 'Europe/Warsaw' },
|
||||
{ label: 'Europe/Zagreb', value: 'Europe/Zagreb' },
|
||||
{ label: 'Europe/Zaporozhye', value: 'Europe/Zaporozhye' },
|
||||
{ label: 'Europe/Zurich', value: 'Europe/Zurich' },
|
||||
{ label: 'Indian/Antananarivo', value: 'Indian/Antananarivo' },
|
||||
{ label: 'Indian/Chagos', value: 'Indian/Chagos' },
|
||||
{ label: 'Indian/Christmas', value: 'Indian/Christmas' },
|
||||
{ label: 'Indian/Cocos', value: 'Indian/Cocos' },
|
||||
{ label: 'Indian/Comoro', value: 'Indian/Comoro' },
|
||||
{ label: 'Indian/Kerguelen', value: 'Indian/Kerguelen' },
|
||||
{ label: 'Indian/Mahe', value: 'Indian/Mahe' },
|
||||
{ label: 'Indian/Maldives', value: 'Indian/Maldives' },
|
||||
{ label: 'Indian/Mauritius', value: 'Indian/Mauritius' },
|
||||
{ label: 'Indian/Mayotte', value: 'Indian/Mayotte' },
|
||||
{ label: 'Indian/Reunion', value: 'Indian/Reunion' },
|
||||
{ label: 'Pacific/Apia', value: 'Pacific/Apia' },
|
||||
{ label: 'Pacific/Auckland', value: 'Pacific/Auckland' },
|
||||
{ label: 'Pacific/Bougainville', value: 'Pacific/Bougainville' },
|
||||
{ label: 'Pacific/Chatham', value: 'Pacific/Chatham' },
|
||||
{ label: 'Pacific/Easter', value: 'Pacific/Easter' },
|
||||
{ label: 'Pacific/Efate', value: 'Pacific/Efate' },
|
||||
{ label: 'Pacific/Enderbury', value: 'Pacific/Enderbury' },
|
||||
{ label: 'Pacific/Fakaofo', value: 'Pacific/Fakaofo' },
|
||||
{ label: 'Pacific/Fiji', value: 'Pacific/Fiji' },
|
||||
{ label: 'Pacific/Funafuti', value: 'Pacific/Funafuti' },
|
||||
{ label: 'Pacific/Galapagos', value: 'Pacific/Galapagos' },
|
||||
{ label: 'Pacific/Gambier', value: 'Pacific/Gambier' },
|
||||
{ label: 'Pacific/Guadalcanal', value: 'Pacific/Guadalcanal' },
|
||||
{ label: 'Pacific/Guam', value: 'Pacific/Guam' },
|
||||
{ label: 'Pacific/Honolulu', value: 'Pacific/Honolulu' },
|
||||
{ label: 'Pacific/Johnston', value: 'Pacific/Johnston' },
|
||||
{ label: 'Pacific/Kiritimati', value: 'Pacific/Kiritimati' },
|
||||
{ label: 'Pacific/Kosrae', value: 'Pacific/Kosrae' },
|
||||
{ label: 'Pacific/Kwajalein', value: 'Pacific/Kwajalein' },
|
||||
{ label: 'Pacific/Majuro', value: 'Pacific/Majuro' },
|
||||
{ label: 'Pacific/Marquesas', value: 'Pacific/Marquesas' },
|
||||
{ label: 'Pacific/Midway', value: 'Pacific/Midway' },
|
||||
{ label: 'Pacific/Nauru', value: 'Pacific/Nauru' },
|
||||
{ label: 'Pacific/Niue', value: 'Pacific/Niue' },
|
||||
{ label: 'Pacific/Norfolk', value: 'Pacific/Norfolk' },
|
||||
{ label: 'Pacific/Noumea', value: 'Pacific/Noumea' },
|
||||
{ label: 'Pacific/Pago_Pago', value: 'Pacific/Pago_Pago' },
|
||||
{ label: 'Pacific/Palau', value: 'Pacific/Palau' },
|
||||
{ label: 'Pacific/Pitcairn', value: 'Pacific/Pitcairn' },
|
||||
{ label: 'Pacific/Ponape', value: 'Pacific/Ponape' },
|
||||
{ label: 'Pacific/Port_Moresby', value: 'Pacific/Port_Moresby' },
|
||||
{ label: 'Pacific/Rarotonga', value: 'Pacific/Rarotonga' },
|
||||
{ label: 'Pacific/Saipan', value: 'Pacific/Saipan' },
|
||||
{ label: 'Pacific/Tahiti', value: 'Pacific/Tahiti' },
|
||||
{ label: 'Pacific/Tarawa', value: 'Pacific/Tarawa' },
|
||||
{ label: 'Pacific/Tongatapu', value: 'Pacific/Tongatapu' },
|
||||
{ label: 'Pacific/Truk', value: 'Pacific/Truk' },
|
||||
{ label: 'Pacific/Wake', value: 'Pacific/Wake' },
|
||||
{ label: 'Pacific/Wallis', value: 'Pacific/Wallis' },
|
||||
];
|
||||
|
||||
export default timezoneOptions;
|
@@ -0,0 +1,43 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
import capitalize from './text/capitalize';
|
||||
import extractEmailAddress from './text/extract-email-address';
|
||||
import extractNumber from './text/extract-number';
|
||||
import findArrayItemByProperty from './utilities/find-array-item-by-property';
|
||||
import formatDateTime from './date-time/format-date-time';
|
||||
import formatNumber from './numbers/format-number';
|
||||
import htmlToMarkdown from './text/html-to-markdown';
|
||||
import lowercase from './text/lowercase';
|
||||
import markdownToHtml from './text/markdown-to-html';
|
||||
import performMathOperation from './numbers/perform-math-operation';
|
||||
import pluralize from './text/pluralize';
|
||||
import randomNumber from './numbers/random-number';
|
||||
import replace from './text/replace';
|
||||
import trimWhitespace from './text/trim-whitespace';
|
||||
import useDefaultValue from './text/use-default-value';
|
||||
|
||||
const options: IJSONObject = {
|
||||
capitalize,
|
||||
extractEmailAddress,
|
||||
extractNumber,
|
||||
findArrayItemByProperty,
|
||||
formatDateTime,
|
||||
formatNumber,
|
||||
htmlToMarkdown,
|
||||
lowercase,
|
||||
markdownToHtml,
|
||||
performMathOperation,
|
||||
pluralize,
|
||||
randomNumber,
|
||||
replace,
|
||||
trimWhitespace,
|
||||
useDefaultValue,
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'List fields after transform',
|
||||
key: 'listTransformOptions',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
return options[$.step.parameters.transform as string];
|
||||
},
|
||||
};
|
@@ -0,0 +1,38 @@
|
||||
const formatNumber = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The number you want to format.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Input Decimal Mark',
|
||||
key: 'inputDecimalMark',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'The decimal mark of the input number.',
|
||||
variables: true,
|
||||
options: [
|
||||
{ label: 'Comma', value: ',' },
|
||||
{ label: 'Period', value: '.' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'To Format',
|
||||
key: 'toFormat',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'The format you want to convert the number to.',
|
||||
variables: true,
|
||||
options: [
|
||||
{ label: 'Comma for grouping & period for decimal', value: '0' },
|
||||
{ label: 'Period for grouping & comma for decimal', value: '1' },
|
||||
{ label: 'Space for grouping & period for decimal', value: '2' },
|
||||
{ label: 'Space for grouping & comma for decimal', value: '3' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default formatNumber;
|
@@ -0,0 +1,36 @@
|
||||
const performMathOperation = [
|
||||
{
|
||||
label: 'Math Operation',
|
||||
key: 'mathOperation',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'The math operation to perform.',
|
||||
variables: true,
|
||||
options: [
|
||||
{ label: 'Add', value: 'add' },
|
||||
{ label: 'Divide', value: 'divide' },
|
||||
{ label: 'Make Negative', value: 'makeNegative' },
|
||||
{ label: 'Multiply', value: 'multiply' },
|
||||
{ label: 'Subtract', value: 'subtract' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Values',
|
||||
key: 'values',
|
||||
type: 'dynamic' as const,
|
||||
required: false,
|
||||
description: 'Add or remove numbers as needed.',
|
||||
fields: [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The number to perform the math operation on.',
|
||||
variables: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default performMathOperation;
|
@@ -0,0 +1,29 @@
|
||||
const randomNumber = [
|
||||
{
|
||||
label: 'Lower range',
|
||||
key: 'lowerRange',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The lowest number to generate.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Upper range',
|
||||
key: 'upperRange',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The highest number to generate.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Decimal points',
|
||||
key: 'decimalPoints',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description:
|
||||
'The number of digits after the decimal point. It can be an integer between 0 and 15.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default randomNumber;
|
@@ -0,0 +1,12 @@
|
||||
const capitalize = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text that will be capitalized.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default capitalize;
|
@@ -0,0 +1,12 @@
|
||||
const extractEmailAddress = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text that will be searched for an email address.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default extractEmailAddress;
|
@@ -0,0 +1,12 @@
|
||||
const extractNumber = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text that will be searched for a number.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default extractNumber;
|
@@ -0,0 +1,12 @@
|
||||
const htmlToMarkdown = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'HTML that will be converted to Markdown.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default htmlToMarkdown;
|
@@ -0,0 +1,12 @@
|
||||
const lowercase = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text that will be lowercased.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default lowercase;
|
@@ -0,0 +1,12 @@
|
||||
const markdownToHtml = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Markdown text that will be converted to HTML.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default markdownToHtml;
|
@@ -0,0 +1,12 @@
|
||||
const pluralize = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text that will be pluralized.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default pluralize;
|
@@ -0,0 +1,28 @@
|
||||
const replace = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text that you want to search for and replace values.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Find',
|
||||
key: 'find',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text that will be searched for.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Replace',
|
||||
key: 'replace',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'Text that will replace the found text.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default replace;
|
@@ -0,0 +1,12 @@
|
||||
const trimWhitespace = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text you want to remove leading and trailing spaces.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default trimWhitespace;
|
@@ -0,0 +1,21 @@
|
||||
const useDefaultValue = [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Text you want to check whether it is empty or not.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Default Value',
|
||||
key: 'defaultValue',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description:
|
||||
'Text that will be used as a default value if the input is empty.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default useDefaultValue;
|
@@ -0,0 +1,28 @@
|
||||
const findArrayItemByProperty = [
|
||||
{
|
||||
label: 'Value',
|
||||
key: 'value',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Array of objects that will be searched.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Property Name',
|
||||
key: 'propertyName',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Property name that will be searched.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Property Value',
|
||||
key: 'propertyValue',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Property value that will be matched.',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default findArrayItemByProperty;
|
0
packages/backend/src/apps/formatter/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/formatter/index.d.ts
vendored
Normal file
16
packages/backend/src/apps/formatter/index.ts
Normal file
16
packages/backend/src/apps/formatter/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import actions from './actions';
|
||||
import dynamicFields from './dynamic-fields';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Formatter',
|
||||
key: 'formatter',
|
||||
iconUrl: '{BASE_URL}/apps/formatter/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/formatter/connection',
|
||||
supportsConnections: false,
|
||||
baseUrl: '',
|
||||
apiBaseUrl: '',
|
||||
primaryColor: '001F52',
|
||||
actions,
|
||||
dynamicFields,
|
||||
});
|
@@ -25,6 +25,12 @@ const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
$.auth.data.accessToken = data.access_token;
|
||||
|
||||
const currentUser = await getCurrentUser($);
|
||||
const screenName = [
|
||||
currentUser.username,
|
||||
$.auth.data.instanceUrl,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' @ ');
|
||||
|
||||
await $.auth.set({
|
||||
clientId: $.auth.data.clientId,
|
||||
@@ -34,7 +40,7 @@ const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
scope: data.scope,
|
||||
tokenType: data.token_type,
|
||||
userId: currentUser.id,
|
||||
screenName: `${currentUser.username} @ ${$.auth.data.instanceUrl}`,
|
||||
screenName,
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -0,0 +1,83 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Create contact',
|
||||
key: 'createContact',
|
||||
description: `Create contact on user's account.`,
|
||||
arguments: [
|
||||
{
|
||||
label: 'Company name',
|
||||
key: 'company',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Email',
|
||||
key: 'email',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'First name',
|
||||
key: 'firstName',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Last name',
|
||||
key: 'lastName',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'Last name',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Phone',
|
||||
key: 'phone',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Website URL',
|
||||
key: 'website',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Owner ID',
|
||||
key: 'hubspotOwnerId',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const company = $.step.parameters.company as string;
|
||||
const email = $.step.parameters.email as string;
|
||||
const firstName = $.step.parameters.firstName as string;
|
||||
const lastName = $.step.parameters.lastName as string;
|
||||
const phone = $.step.parameters.phone as string;
|
||||
const website = $.step.parameters.website as string;
|
||||
const hubspotOwnerId = $.step.parameters.hubspotOwnerId as string;
|
||||
|
||||
const response = await $.http.post(`crm/v3/objects/contacts`, {
|
||||
properties: {
|
||||
company,
|
||||
email,
|
||||
firstname: firstName,
|
||||
lastname: lastName,
|
||||
phone,
|
||||
website,
|
||||
hubspot_owner_id: hubspotOwnerId,
|
||||
},
|
||||
});
|
||||
|
||||
$.setActionItem({ raw: response.data });
|
||||
},
|
||||
});
|
3
packages/backend/src/apps/hubspot/actions/index.ts
Normal file
3
packages/backend/src/apps/hubspot/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import createContact from './create-contact';
|
||||
|
||||
export default [ createContact ];
|
8
packages/backend/src/apps/hubspot/assets/favicon.svg
Normal file
8
packages/backend/src/apps/hubspot/assets/favicon.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="27px" height="28px" viewBox="0 0 27 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g fill="#f95c35">
|
||||
<path d="M19.614233,20.1771162 C17.5228041,20.1771162 15.8274241,18.4993457 15.8274241,16.4299995 C15.8274241,14.3602937 17.5228041,12.6825232 19.614233,12.6825232 C21.7056619,12.6825232 23.4010418,14.3602937 23.4010418,16.4299995 C23.4010418,18.4993457 21.7056619,20.1771162 19.614233,20.1771162 M20.7478775,9.21551429 L20.7478775,5.88190722 C21.6271788,5.47091457 22.243053,4.59067833 22.243053,3.56912967 L22.243053,3.49218091 C22.243053,2.08229273 21.0774338,0.928780545 19.6527478,0.928780545 L19.5753548,0.928780545 C18.1506688,0.928780545 16.9850496,2.08229273 16.9850496,3.49218091 L16.9850496,3.56912967 C16.9850496,4.59067833 17.6009238,5.47127414 18.4802251,5.88226679 L18.4802251,9.21551429 C17.1710836,9.4157968 15.9749432,9.95012321 14.9884545,10.7365107 L5.73944086,3.61659339 C5.80048326,3.3846684 5.84335828,3.14591151 5.84372163,2.89492912 C5.84517502,1.29842223 4.53930368,0.00215931486 2.92531356,1.87311107e-06 C1.31205014,-0.00179599501 0.00181863138,1.29087118 1.8932965e-06,2.88773765 C-0.00181484479,4.48460412 1.30405649,5.78086703 2.91804661,5.7826649 C3.44381061,5.78338405 3.93069642,5.63559929 4.35726652,5.39540411 L13.4551275,12.3995387 C12.6815604,13.5552084 12.2281026,14.9395668 12.2281026,16.4299995 C12.2281026,17.9901894 12.7262522,19.433518 13.5677653,20.6204705 L10.8012365,23.3586237 C10.5825013,23.2935408 10.3557723,23.2482346 10.1152362,23.2482346 C8.78938076,23.2482346 7.71423516,24.3118533 7.71423516,25.6239375 C7.71423516,26.9363812 8.78938076,28 10.1152362,28 C11.441455,28 12.5162373,26.9363812 12.5162373,25.6239375 C12.5162373,25.3866189 12.4704555,25.1618854 12.4046896,24.9454221 L15.1414238,22.2371135 C16.3837093,23.1752411 17.9308435,23.7390526 19.614233,23.7390526 C23.6935367,23.7390526 27,20.466573 27,16.4299995 C27,12.7756527 24.2872467,9.7566726 20.7478775,9.21551429"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
20
packages/backend/src/apps/hubspot/auth/generate-auth-url.ts
Normal file
20
packages/backend/src/apps/hubspot/auth/generate-auth-url.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
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.clientId as string,
|
||||
redirect_uri: callbackUrl,
|
||||
scope: scopes.join(' '),
|
||||
});
|
||||
|
||||
const url = `https://app.hubspot.com/oauth/authorize?${searchParams.toString()}`;
|
||||
|
||||
await $.auth.set({ url });
|
||||
}
|
48
packages/backend/src/apps/hubspot/auth/index.ts
Normal file
48
packages/backend/src/apps/hubspot/auth/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import generateAuthUrl from './generate-auth-url';
|
||||
import verifyCredentials from './verify-credentials';
|
||||
import isStillVerified from './is-still-verified';
|
||||
import refreshToken from './refresh-token';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'oAuthRedirectUrl',
|
||||
label: 'OAuth Redirect URL',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
value: '{WEB_APP_URL}/app/hubspot/connections/add',
|
||||
placeholder: null,
|
||||
description:
|
||||
'When asked to input an OAuth callback or redirect URL in HubSpot OAuth, enter the URL above.',
|
||||
clickToCopy: true,
|
||||
},
|
||||
{
|
||||
key: 'clientId',
|
||||
label: 'Client ID',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'clientSecret',
|
||||
label: 'Client Secret',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
],
|
||||
|
||||
generateAuthUrl,
|
||||
verifyCredentials,
|
||||
isStillVerified,
|
||||
refreshToken,
|
||||
};
|
10
packages/backend/src/apps/hubspot/auth/is-still-verified.ts
Normal file
10
packages/backend/src/apps/hubspot/auth/is-still-verified.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import getAccessTokenInfo from '../common/get-access-token-info';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
await getAccessTokenInfo($);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
28
packages/backend/src/apps/hubspot/auth/refresh-token.ts
Normal file
28
packages/backend/src/apps/hubspot/auth/refresh-token.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { IGlobalVariable, IField } from '@automatisch/types';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
const refreshToken = async ($: IGlobalVariable) => {
|
||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
|
||||
const callbackUrl = oauthRedirectUrlField.value as string;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
grant_type: 'refresh_token',
|
||||
client_id: $.auth.data.clientId as string,
|
||||
client_secret: $.auth.data.clientSecret as string,
|
||||
redirect_uri: callbackUrl,
|
||||
refresh_token: $.auth.data.refreshToken as string,
|
||||
});
|
||||
|
||||
const { data } = await $.http.post('/oauth/v1/token', params.toString());
|
||||
|
||||
await $.auth.set({
|
||||
accessToken: data.access_token,
|
||||
expiresIn: data.expires_in,
|
||||
refreshToken: data.refresh_token,
|
||||
});
|
||||
};
|
||||
|
||||
export default refreshToken;
|
52
packages/backend/src/apps/hubspot/auth/verify-credentials.ts
Normal file
52
packages/backend/src/apps/hubspot/auth/verify-credentials.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { IGlobalVariable, IField } from '@automatisch/types';
|
||||
import { URLSearchParams } from 'url';
|
||||
import getAccessTokenInfo from '../common/get-access-token-info';
|
||||
|
||||
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({
|
||||
grant_type: 'authorization_code',
|
||||
client_id: $.auth.data.clientId as string,
|
||||
client_secret: $.auth.data.clientSecret as string,
|
||||
redirect_uri: callbackUrl,
|
||||
code: $.auth.data.code as string,
|
||||
});
|
||||
|
||||
const { data: verifiedCredentials } = await $.http.post(
|
||||
'/oauth/v1/token',
|
||||
params.toString()
|
||||
);
|
||||
|
||||
const {
|
||||
access_token: accessToken,
|
||||
refresh_token: refreshToken,
|
||||
expires_in: expiresIn,
|
||||
} = verifiedCredentials;
|
||||
|
||||
await $.auth.set({
|
||||
accessToken,
|
||||
refreshToken,
|
||||
expiresIn,
|
||||
});
|
||||
|
||||
const accessTokenInfo = await getAccessTokenInfo($);
|
||||
|
||||
await $.auth.set({
|
||||
screenName: accessTokenInfo.user,
|
||||
hubDomain: accessTokenInfo.hub_domain,
|
||||
scopes: accessTokenInfo.scopes,
|
||||
scopeToScopeGroupPks: accessTokenInfo.scope_to_scope_group_pks,
|
||||
trialScopes: accessTokenInfo.trial_scopes,
|
||||
trialScopeToScoreGroupPks: accessTokenInfo.trial_scope_to_scope_group_pks,
|
||||
hubId: accessTokenInfo.hub_id,
|
||||
appId: accessTokenInfo.app_id,
|
||||
userId: accessTokenInfo.user_id,
|
||||
expiresIn: accessTokenInfo.expires_in,
|
||||
tokenType: accessTokenInfo.token_type,
|
||||
});
|
||||
};
|
||||
|
||||
export default verifyCredentials;
|
14
packages/backend/src/apps/hubspot/common/add-auth-header.ts
Normal file
14
packages/backend/src/apps/hubspot/common/add-auth-header.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
if (requestConfig.additionalProperties?.skipAddingAuthHeader) return requestConfig;
|
||||
|
||||
if ($.auth.data?.accessToken) {
|
||||
const authorizationHeader = `Bearer ${$.auth.data.accessToken}`;
|
||||
requestConfig.headers.Authorization = authorizationHeader;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
@@ -0,0 +1,11 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
const getAccessTokenInfo = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
||||
const response = await $.http.get(
|
||||
`/oauth/v1/access-tokens/${$.auth.data.accessToken}`
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default getAccessTokenInfo;
|
3
packages/backend/src/apps/hubspot/common/scopes.ts
Normal file
3
packages/backend/src/apps/hubspot/common/scopes.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const scopes = ['crm.objects.contacts.read', 'crm.objects.contacts.write'];
|
||||
|
||||
export default scopes;
|
0
packages/backend/src/apps/hubspot/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/hubspot/index.d.ts
vendored
Normal file
18
packages/backend/src/apps/hubspot/index.ts
Normal file
18
packages/backend/src/apps/hubspot/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import actions from './actions';
|
||||
import auth from './auth';
|
||||
|
||||
export default defineApp({
|
||||
name: 'HubSpot',
|
||||
key: 'hubspot',
|
||||
iconUrl: '{BASE_URL}/apps/hubspot/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/hubspot/connection',
|
||||
supportsConnections: true,
|
||||
baseUrl: 'https://www.hubspot.com',
|
||||
apiBaseUrl: 'https://api.hubapi.com',
|
||||
primaryColor: 'F95C35',
|
||||
beforeRequest: [addAuthHeader],
|
||||
auth,
|
||||
actions,
|
||||
});
|
@@ -4,9 +4,13 @@ import bcrypt from 'bcrypt';
|
||||
|
||||
const getInternalId = async (item: IJSONObject): Promise<string> => {
|
||||
if (item.guid) {
|
||||
return item.guid.toString();
|
||||
return typeof item.guid === 'object'
|
||||
? (item.guid as IJSONObject)['#text'].toString()
|
||||
: item.guid.toString();
|
||||
} else if (item.id) {
|
||||
return item.id.toString();
|
||||
return typeof item.id === 'object'
|
||||
? (item.id as IJSONObject)['#text'].toString()
|
||||
: item.id.toString();
|
||||
}
|
||||
|
||||
return await hashItem(JSON.stringify(item));
|
||||
|
6
packages/backend/src/apps/wordpress/assets/favicon.svg
Normal file
6
packages/backend/src/apps/wordpress/assets/favicon.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<svg width="256px" height="255px" viewBox="0 0 256 255" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g fill="#464342">
|
||||
<path d="M18.1239675,127.500488 C18.1239675,170.795707 43.284813,208.211252 79.7700163,225.941854 L27.5938862,82.985626 C21.524813,96.5890081 18.1239675,111.643057 18.1239675,127.500488 L18.1239675,127.500488 Z M201.345041,121.980878 C201.345041,108.462829 196.489366,99.1011382 192.324683,91.8145041 C186.780098,82.8045528 181.583089,75.1745041 181.583089,66.1645528 C181.583089,56.1097886 189.208976,46.7501789 199.950569,46.7501789 C200.435512,46.7501789 200.89548,46.8105366 201.367935,46.8375935 C181.907772,29.0091707 155.981008,18.1239675 127.50465,18.1239675 C89.2919675,18.1239675 55.6727154,37.7298211 36.1147317,67.4258211 C38.6809756,67.5028293 41.0994472,67.5569431 43.1536911,67.5569431 C54.5946016,67.5569431 72.3043902,66.1687154 72.3043902,66.1687154 C78.2007154,65.8211382 78.8958699,74.4814309 73.0057886,75.1786667 C73.0057886,75.1786667 67.0803252,75.8759024 60.4867642,76.2213984 L100.318699,194.699447 L124.25574,122.909138 L107.214049,76.2172358 C101.323967,75.8717398 95.744,75.1745041 95.744,75.1745041 C89.8497561,74.8290081 90.540748,65.8169756 96.4349919,66.1645528 C96.4349919,66.1645528 114.498602,67.5527805 125.246439,67.5527805 C136.685268,67.5527805 154.397138,66.1645528 154.397138,66.1645528 C160.297626,65.8169756 160.990699,74.4772683 155.098537,75.1745041 C155.098537,75.1745041 149.160585,75.8717398 142.579512,76.2172358 L182.107577,193.798244 L193.017756,157.340098 C197.746472,142.211122 201.345041,131.34465 201.345041,121.980878 L201.345041,121.980878 Z M129.42361,137.068228 L96.6056585,232.43135 C106.404423,235.31187 116.76722,236.887415 127.50465,236.887415 C140.242211,236.887415 152.457366,234.685398 163.827512,230.68722 C163.534049,230.218927 163.267642,229.721496 163.049106,229.180358 L129.42361,137.068228 L129.42361,137.068228 Z M223.481756,75.0225691 C223.95213,78.5066667 224.218537,82.2467642 224.218537,86.2699187 C224.218537,97.3694959 222.145561,109.846894 215.901659,125.448325 L182.490537,222.04774 C215.00878,203.085008 236.881171,167.854829 236.881171,127.502569 C236.883252,108.485724 232.025496,90.603187 223.481756,75.0225691 L223.481756,75.0225691 Z M127.50465,0 C57.2003902,0 0,57.1962276 0,127.500488 C0,197.813073 57.2003902,255.00722 127.50465,255.00722 C197.806829,255.00722 255.015545,197.813073 255.015545,127.500488 C255.013463,57.1962276 197.806829,0 127.50465,0 L127.50465,0 Z M127.50465,249.162927 C60.4243252,249.162927 5.84637398,194.584976 5.84637398,127.500488 C5.84637398,60.4201626 60.4222439,5.84637398 127.50465,5.84637398 C194.582894,5.84637398 249.156683,60.4201626 249.156683,127.500488 C249.156683,194.584976 194.582894,249.162927 127.50465,249.162927 L127.50465,249.162927 Z"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,24 @@
|
||||
import { URL, URLSearchParams } from 'node:url';
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
import appConfig from '../../../config/app';
|
||||
import getInstanceUrl from '../common/get-instance-url';
|
||||
|
||||
export default async function generateAuthUrl($: IGlobalVariable) {
|
||||
const successUrl = new URL(
|
||||
'/app/wordpress/connections/add',
|
||||
appConfig.webAppUrl
|
||||
).toString();
|
||||
const baseUrl = getInstanceUrl($);
|
||||
|
||||
const searchParams = new URLSearchParams({
|
||||
app_name: 'automatisch',
|
||||
success_url: successUrl,
|
||||
});
|
||||
|
||||
const url = new URL(`/wp-admin/authorize-application.php?${searchParams}`, baseUrl).toString();
|
||||
|
||||
await $.auth.set({
|
||||
url,
|
||||
});
|
||||
}
|
24
packages/backend/src/apps/wordpress/auth/index.ts
Normal file
24
packages/backend/src/apps/wordpress/auth/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import generateAuthUrl from './generate-auth-url';
|
||||
import isStillVerified from './is-still-verified';
|
||||
import verifyCredentials from './verify-credentials';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'instanceUrl',
|
||||
label: 'WordPress instance URL',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Your WordPress instance URL.',
|
||||
docUrl: 'https://automatisch.io/docs/wordpress#instance-url',
|
||||
clickToCopy: true,
|
||||
},
|
||||
],
|
||||
|
||||
generateAuthUrl,
|
||||
isStillVerified,
|
||||
verifyCredentials,
|
||||
};
|
@@ -0,0 +1,9 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
await $.http.get('?rest_route=/wp/v2/settings');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
@@ -0,0 +1,24 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
const instanceUrl = $.auth.data.instanceUrl as string;
|
||||
const password = $.auth.data.password as string;
|
||||
const siteUrl = $.auth.data.site_url as string;
|
||||
const url = $.auth.data.url as string;
|
||||
const userLogin = $.auth.data.user_login as string;
|
||||
|
||||
if (!password) {
|
||||
throw new Error('Failed while authorizing!');
|
||||
}
|
||||
|
||||
await $.auth.set({
|
||||
screenName: `${userLogin} @ ${siteUrl}`,
|
||||
instanceUrl,
|
||||
password,
|
||||
siteUrl,
|
||||
url,
|
||||
userLogin,
|
||||
});
|
||||
};
|
||||
|
||||
export default verifyCredentials;
|
@@ -0,0 +1,17 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
const userLogin = $.auth.data.userLogin as string;
|
||||
const password = $.auth.data.password as string;
|
||||
|
||||
if (userLogin && password) {
|
||||
requestConfig.auth = {
|
||||
username: userLogin,
|
||||
password,
|
||||
};
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
@@ -0,0 +1,7 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const getInstanceUrl = ($: IGlobalVariable): string => {
|
||||
return $.auth.data.instanceUrl as string;
|
||||
};
|
||||
|
||||
export default getInstanceUrl;
|
12
packages/backend/src/apps/wordpress/common/set-base-url.ts
Normal file
12
packages/backend/src/apps/wordpress/common/set-base-url.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const setBaseUrl: TBeforeRequest = ($, requestConfig) => {
|
||||
const instanceUrl = $.auth.data.instanceUrl as string;
|
||||
if (instanceUrl) {
|
||||
requestConfig.baseURL = instanceUrl;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default setBaseUrl;
|
@@ -0,0 +1,3 @@
|
||||
import listStatuses from './list-statuses';
|
||||
|
||||
export default [listStatuses];
|
@@ -0,0 +1,37 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
type Status = {
|
||||
slug: string;
|
||||
name: string;
|
||||
};
|
||||
type Statuses = Record<string, Status>;
|
||||
|
||||
export default {
|
||||
name: 'List statuses',
|
||||
key: 'listStatuses',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const statuses: {
|
||||
data: IJSONObject[];
|
||||
} = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
const { data } = await $.http.get<Statuses>('?rest_route=/wp/v2/statuses');
|
||||
|
||||
if (!data) return statuses;
|
||||
|
||||
const values = Object.values(data);
|
||||
|
||||
if (!values?.length) return statuses;
|
||||
|
||||
for (const status of values) {
|
||||
statuses.data.push({
|
||||
value: status.slug,
|
||||
name: status.name,
|
||||
});
|
||||
}
|
||||
|
||||
return statuses;
|
||||
},
|
||||
};
|
0
packages/backend/src/apps/wordpress/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/wordpress/index.d.ts
vendored
Normal file
21
packages/backend/src/apps/wordpress/index.ts
Normal file
21
packages/backend/src/apps/wordpress/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import setBaseUrl from './common/set-base-url';
|
||||
import auth from './auth';
|
||||
import triggers from './triggers';
|
||||
import dynamicData from './dynamic-data';
|
||||
|
||||
export default defineApp({
|
||||
name: 'WordPress',
|
||||
key: 'wordpress',
|
||||
iconUrl: '{BASE_URL}/apps/wordpress/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/wordpress/connection',
|
||||
supportsConnections: true,
|
||||
baseUrl: 'https://wordpress.com',
|
||||
apiBaseUrl: '',
|
||||
primaryColor: '464342',
|
||||
beforeRequest: [setBaseUrl, addAuthHeader],
|
||||
auth,
|
||||
triggers,
|
||||
dynamicData,
|
||||
});
|
5
packages/backend/src/apps/wordpress/triggers/index.ts
Normal file
5
packages/backend/src/apps/wordpress/triggers/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import newComment from './new-comment';
|
||||
import newPage from './new-page';
|
||||
import newPost from './new-post';
|
||||
|
||||
export default [newComment, newPage, newPost];
|
@@ -0,0 +1,58 @@
|
||||
import defineTrigger from '../../../../helpers/define-trigger';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'New comment',
|
||||
key: 'newComment',
|
||||
description: 'Triggers when a new comment is created.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Status',
|
||||
key: 'status',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
options: [
|
||||
{ label: 'Approve', value: 'approve' },
|
||||
{ label: 'Unapprove', value: 'hold' },
|
||||
{ label: 'Spam', value: 'spam' },
|
||||
{ label: 'Trash', value: 'trash' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const params = {
|
||||
per_page: 100,
|
||||
page: 1,
|
||||
order: 'desc',
|
||||
orderby: 'date',
|
||||
status: $.step.parameters.status || '',
|
||||
};
|
||||
|
||||
let totalPages = 1;
|
||||
do {
|
||||
const { data, headers } = await $.http.get(
|
||||
'?rest_route=/wp/v2/comments',
|
||||
{
|
||||
params,
|
||||
}
|
||||
);
|
||||
|
||||
params.page = params.page + 1;
|
||||
totalPages = Number(headers['x-wp-totalpages']);
|
||||
|
||||
if (data.length) {
|
||||
for (const page of data) {
|
||||
const dataItem = {
|
||||
raw: page,
|
||||
meta: {
|
||||
internalId: page.id.toString(),
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
}
|
||||
}
|
||||
} while (params.page <= totalPages);
|
||||
},
|
||||
});
|
@@ -0,0 +1,59 @@
|
||||
import defineTrigger from '../../../../helpers/define-trigger';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'New page',
|
||||
key: 'newPage',
|
||||
description: 'Triggers when a new page is created.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Status',
|
||||
key: 'status',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listStatuses',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const params = {
|
||||
per_page: 100,
|
||||
page: 1,
|
||||
order: 'desc',
|
||||
orderby: 'date',
|
||||
status: $.step.parameters.status || '',
|
||||
};
|
||||
|
||||
let totalPages = 1;
|
||||
do {
|
||||
const { data, headers } = await $.http.get('?rest_route=/wp/v2/pages', {
|
||||
params,
|
||||
});
|
||||
|
||||
params.page = params.page + 1;
|
||||
totalPages = Number(headers['x-wp-totalpages']);
|
||||
|
||||
if (data.length) {
|
||||
for (const page of data) {
|
||||
const dataItem = {
|
||||
raw: page,
|
||||
meta: {
|
||||
internalId: page.id.toString(),
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
}
|
||||
}
|
||||
} while (params.page <= totalPages);
|
||||
},
|
||||
});
|
@@ -0,0 +1,60 @@
|
||||
import defineTrigger from '../../../../helpers/define-trigger';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'New post',
|
||||
key: 'newPost',
|
||||
description: 'Triggers when a new post is created.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Status',
|
||||
key: 'status',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listStatuses',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const params = {
|
||||
per_page: 100,
|
||||
page: 1,
|
||||
order: 'desc',
|
||||
orderby: 'date',
|
||||
status: $.step.parameters.status || '',
|
||||
};
|
||||
|
||||
let totalPages = 1;
|
||||
do {
|
||||
const {
|
||||
data,
|
||||
headers
|
||||
} = await $.http.get('?rest_route=/wp/v2/posts', { params });
|
||||
|
||||
params.page = params.page + 1;
|
||||
totalPages = Number(headers['x-wp-totalpages']);
|
||||
|
||||
if (data.length) {
|
||||
for (const post of data) {
|
||||
const dataItem = {
|
||||
raw: post,
|
||||
meta: {
|
||||
internalId: post.id.toString(),
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
}
|
||||
}
|
||||
} while (params.page <= totalPages);
|
||||
},
|
||||
});
|
@@ -1,6 +1,12 @@
|
||||
import { URL } from 'node:url';
|
||||
import * as dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
import path from 'path';
|
||||
|
||||
if (process.env.APP_ENV === 'test') {
|
||||
dotenv.config({ path: path.resolve(__dirname, '../../.env.test') });
|
||||
} else {
|
||||
dotenv.config();
|
||||
}
|
||||
|
||||
type AppConfig = {
|
||||
host: string;
|
||||
|
@@ -0,0 +1,15 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
return knex.schema.createTable('config', (table) => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
||||
table.string('key').unique().notNullable();
|
||||
table.jsonb('value').notNullable().defaultTo({});
|
||||
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
return knex.schema.dropTable('config');
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
const getPermissionForRole = (
|
||||
roleId: string,
|
||||
subject: string,
|
||||
actions: string[]
|
||||
) =>
|
||||
actions.map((action) => ({
|
||||
role_id: roleId,
|
||||
subject,
|
||||
action,
|
||||
conditions: [],
|
||||
}));
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const role = (await knex('roles')
|
||||
.first(['id', 'key'])
|
||||
.where({ key: 'admin' })
|
||||
.limit(1)) as { id: string; key: string };
|
||||
|
||||
await knex('permissions').insert(
|
||||
getPermissionForRole(role.id, 'Config', [
|
||||
'update',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex('permissions').where({ subject: 'Config' }).delete();
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
return knex.schema.createTable(
|
||||
'saml_auth_providers_role_mappings',
|
||||
(table) => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
||||
table
|
||||
.uuid('saml_auth_provider_id')
|
||||
.references('id')
|
||||
.inTable('saml_auth_providers');
|
||||
table.uuid('role_id').references('id').inTable('roles');
|
||||
table.string('remote_role_name').notNullable();
|
||||
|
||||
table.unique(['saml_auth_provider_id', 'remote_role_name']);
|
||||
|
||||
table.timestamps(true, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
return knex.schema.dropTable('saml_auth_providers_role_mappings');
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
return knex.schema.createTable('app_configs', (table) => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
||||
table.string('key').unique().notNullable();
|
||||
table.boolean('allow_custom_connection').notNullable().defaultTo(false);
|
||||
table.boolean('shared').notNullable().defaultTo(false);
|
||||
table.boolean('disabled').notNullable().defaultTo(false);
|
||||
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
return knex.schema.dropTable('app_configs');
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
return knex.schema.createTable('app_auth_clients', (table) => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
||||
table.string('name').unique().notNullable();
|
||||
table.uuid('app_config_id').notNullable().references('id').inTable('app_configs');
|
||||
table.text('auth_defaults').notNullable();
|
||||
table.boolean('active').notNullable().defaultTo(false);
|
||||
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
return knex.schema.dropTable('app_auth_clients');
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.table('connections', async (table) => {
|
||||
table.uuid('app_auth_client_id').references('id').inTable('app_auth_clients');
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
return await knex.schema.table('connections', (table) => {
|
||||
table.dropColumn('app_auth_client_id');
|
||||
});
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
const getPermissionForRole = (
|
||||
roleId: string,
|
||||
subject: string,
|
||||
actions: string[]
|
||||
) =>
|
||||
actions.map((action) => ({
|
||||
role_id: roleId,
|
||||
subject,
|
||||
action,
|
||||
conditions: [],
|
||||
}));
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const role = (await knex('roles')
|
||||
.first(['id', 'key'])
|
||||
.where({ key: 'admin' })
|
||||
.limit(1)) as { id: string; key: string };
|
||||
|
||||
await knex('permissions').insert(
|
||||
getPermissionForRole(role.id, 'App', [
|
||||
'create',
|
||||
'read',
|
||||
'delete',
|
||||
'update',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex('permissions').where({ subject: 'App' }).delete();
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const role = await knex('roles')
|
||||
.select('id')
|
||||
.whereIn('key', ['user', 'admin'])
|
||||
.orderBy('key', 'desc')
|
||||
.limit(1)
|
||||
.first();
|
||||
|
||||
if (role) {
|
||||
// backfill nulls
|
||||
await knex('users').whereNull('role_id').update({ role_id: role.id });
|
||||
}
|
||||
|
||||
return await knex.schema.alterTable('users', (table) => {
|
||||
table.uuid('role_id').notNullable().alter();
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
return await knex.schema.alterTable('users', (table) => {
|
||||
table.uuid('role_id').nullable().alter();
|
||||
});
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const users = await knex('users').whereNotNull('deleted_at');
|
||||
const userIds = users.map((user) => user.id);
|
||||
|
||||
const flows = await knex('flows').whereIn('user_id', userIds);
|
||||
const flowIds = flows.map((flow) => flow.id);
|
||||
const executions = await knex('executions').whereIn('flow_id', flowIds);
|
||||
const executionIds = executions.map((execution) => execution.id);
|
||||
|
||||
await knex('execution_steps').whereIn('execution_id', executionIds).update({
|
||||
deleted_at: knex.fn.now(),
|
||||
});
|
||||
|
||||
await knex('executions').whereIn('id', executionIds).update({
|
||||
deleted_at: knex.fn.now(),
|
||||
});
|
||||
|
||||
await knex('steps').whereIn('flow_id', flowIds).update({
|
||||
deleted_at: knex.fn.now(),
|
||||
});
|
||||
|
||||
await knex('flows').whereIn('id', flowIds).update({
|
||||
deleted_at: knex.fn.now(),
|
||||
});
|
||||
|
||||
await knex('connections').whereIn('user_id', userIds).update({
|
||||
deleted_at: knex.fn.now(),
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(): Promise<void> {
|
||||
// void
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex('permissions')
|
||||
.where(knex.raw('conditions::text'), '=', knex.raw("'{}'::text"))
|
||||
.update('conditions', JSON.stringify([]));
|
||||
}
|
||||
|
||||
export async function down(): Promise<void> {
|
||||
// void
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
import createAppAuthClient from './mutations/create-app-auth-client.ee';
|
||||
import createAppConfig from './mutations/create-app-config.ee';
|
||||
import createConnection from './mutations/create-connection';
|
||||
import createFlow from './mutations/create-flow';
|
||||
import createRole from './mutations/create-role.ee';
|
||||
@@ -17,6 +19,9 @@ import login from './mutations/login';
|
||||
import registerUser from './mutations/register-user.ee';
|
||||
import resetConnection from './mutations/reset-connection';
|
||||
import resetPassword from './mutations/reset-password.ee';
|
||||
import updateAppAuthClient from './mutations/update-app-auth-client.ee';
|
||||
import updateAppConfig from './mutations/update-app-config.ee';
|
||||
import updateConfig from './mutations/update-config.ee';
|
||||
import updateConnection from './mutations/update-connection';
|
||||
import updateCurrentUser from './mutations/update-current-user';
|
||||
import updateFlow from './mutations/update-flow';
|
||||
@@ -24,10 +29,13 @@ import updateFlowStatus from './mutations/update-flow-status';
|
||||
import updateRole from './mutations/update-role.ee';
|
||||
import updateStep from './mutations/update-step';
|
||||
import updateUser from './mutations/update-user.ee';
|
||||
import upsertSamlAuthProvider from './mutations/upsert-saml-auth-provider.ee';
|
||||
import upsertSamlAuthProvidersRoleMappings from './mutations/upsert-saml-auth-providers-role-mappings.ee';
|
||||
import verifyConnection from './mutations/verify-connection';
|
||||
import createSamlAuthProvider from './mutations/create-saml-auth-provider.ee';
|
||||
|
||||
const mutationResolvers = {
|
||||
createAppAuthClient,
|
||||
createAppConfig,
|
||||
createConnection,
|
||||
createFlow,
|
||||
createRole,
|
||||
@@ -47,15 +55,19 @@ const mutationResolvers = {
|
||||
registerUser,
|
||||
resetConnection,
|
||||
resetPassword,
|
||||
updateAppAuthClient,
|
||||
updateAppConfig,
|
||||
updateConfig,
|
||||
updateConnection,
|
||||
updateCurrentUser,
|
||||
updateUser,
|
||||
updateFlow,
|
||||
updateFlowStatus,
|
||||
updateRole,
|
||||
updateStep,
|
||||
updateUser,
|
||||
upsertSamlAuthProvider,
|
||||
upsertSamlAuthProvidersRoleMappings,
|
||||
verifyConnection,
|
||||
createSamlAuthProvider,
|
||||
};
|
||||
|
||||
export default mutationResolvers;
|
||||
|
@@ -0,0 +1,35 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
appConfigId: string;
|
||||
name: string;
|
||||
formattedAuthDefaults?: IJSONObject;
|
||||
active?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const createAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.findById(params.input.appConfigId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const appAuthClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.insert(
|
||||
params.input
|
||||
);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default createAppAuthClient;
|
@@ -0,0 +1,36 @@
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
allowCustomConnection?: boolean;
|
||||
shared?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const key = params.input.key;
|
||||
|
||||
const app = await App.findOneByKey(key);
|
||||
|
||||
if (!app) throw new Error('The app cannot be found!');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.insert(
|
||||
params.input
|
||||
);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default createAppConfig;
|
@@ -1,13 +1,16 @@
|
||||
import App from '../../models/app';
|
||||
import Context from '../../types/express/context';
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
appAuthClientId: string;
|
||||
formattedData: IJSONObject;
|
||||
};
|
||||
};
|
||||
|
||||
const createConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
@@ -15,13 +18,42 @@ const createConnection = async (
|
||||
) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
await App.findOneByKey(params.input.key);
|
||||
const { key, appAuthClientId } = params.input;
|
||||
|
||||
return await context.currentUser.$relatedQuery('connections').insert({
|
||||
key: params.input.key,
|
||||
formattedData: params.input.formattedData,
|
||||
verified: false,
|
||||
});
|
||||
const app = await App.findOneByKey(key);
|
||||
|
||||
const appConfig = await AppConfig.query().findOne({ key });
|
||||
|
||||
let formattedData = params.input.formattedData;
|
||||
if (appConfig) {
|
||||
if (appConfig.disabled) throw new Error('This application has been disabled for new connections!');
|
||||
|
||||
if (!appConfig.allowCustomConnection && formattedData) throw new Error(`Custom connections cannot be created for ${app.name}!`);
|
||||
|
||||
if (appConfig.shared && !formattedData) {
|
||||
const authClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.findById(appAuthClientId)
|
||||
.where({
|
||||
active: true
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
formattedData = authClient.formattedAuthDefaults;
|
||||
}
|
||||
}
|
||||
|
||||
const createdConnection = await context
|
||||
.currentUser
|
||||
.$relatedQuery('connections')
|
||||
.insert({
|
||||
key,
|
||||
appAuthClientId,
|
||||
formattedData,
|
||||
verified: false,
|
||||
});
|
||||
|
||||
return createdConnection;
|
||||
};
|
||||
|
||||
export default createConnection;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user