Compare commits
58 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
35ef66e3fd | ||
![]() |
44f815db02 | ||
![]() |
5224749725 | ||
![]() |
9330e0976b | ||
![]() |
8f72c8c8fa | ||
![]() |
7a632c2ab9 | ||
![]() |
d1e7b6b9eb | ||
![]() |
ba17cde59b | ||
![]() |
2f0125e871 | ||
![]() |
b2dba22674 | ||
![]() |
a3d50e2766 | ||
![]() |
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 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -1,5 +1,11 @@
|
|||||||
name: Automatisch CI
|
name: Automatisch CI
|
||||||
on: [push]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linter:
|
linter:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
11
.github/workflows/playwright.yml
vendored
11
.github/workflows/playwright.yml
vendored
@@ -1,8 +1,9 @@
|
|||||||
name: Automatisch UI Tests
|
name: Automatisch UI Tests
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
schedule:
|
branches:
|
||||||
- cron: '0 12 * * *'
|
- main
|
||||||
|
pull_request:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -75,9 +76,9 @@ jobs:
|
|||||||
- name: Run Playwright tests
|
- name: Run Playwright tests
|
||||||
working-directory: ./packages/e2e-tests
|
working-directory: ./packages/e2e-tests
|
||||||
env:
|
env:
|
||||||
LOGIN_EMAIL: ${{ secrets.LOGIN_EMAIL }}
|
LOGIN_EMAIL: user@automatisch.io
|
||||||
LOGIN_PASSWORD: ${{ secrets.LOGIN_PASSWORD }}
|
LOGIN_PASSWORD: sample
|
||||||
BASE_URL: ${{ vars.E2E_BASE_URL }}
|
BASE_URL: http://localhost:3000
|
||||||
run: yarn test
|
run: yarn test
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: always()
|
if: always()
|
||||||
|
1
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
16.15.0
|
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'],
|
||||||
|
};
|
@@ -9,7 +9,8 @@
|
|||||||
"build": "tsc && yarn copy-statics",
|
"build": "tsc && yarn copy-statics",
|
||||||
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
|
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
|
||||||
"start": "node dist/src/server.js",
|
"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",
|
"lint": "eslint . --ignore-path ../../.eslintignore",
|
||||||
"db:create": "ts-node ./bin/database/create.ts",
|
"db:create": "ts-node ./bin/database/create.ts",
|
||||||
"db:seed:user": "ts-node ./bin/database/seed-user.ts",
|
"db:seed:user": "ts-node ./bin/database/seed-user.ts",
|
||||||
@@ -31,9 +32,11 @@
|
|||||||
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
||||||
"@sentry/node": "^7.42.0",
|
"@sentry/node": "^7.42.0",
|
||||||
"@sentry/tracing": "^7.42.0",
|
"@sentry/tracing": "^7.42.0",
|
||||||
|
"@types/accounting": "^0.4.2",
|
||||||
"@types/luxon": "^2.3.1",
|
"@types/luxon": "^2.3.1",
|
||||||
"@types/passport": "^1.0.12",
|
"@types/passport": "^1.0.12",
|
||||||
"@types/xmlrpc": "^1.3.7",
|
"@types/xmlrpc": "^1.3.7",
|
||||||
|
"accounting": "^0.4.1",
|
||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"axios": "0.24.0",
|
"axios": "0.24.0",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
@@ -69,6 +72,7 @@
|
|||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"pg": "^8.7.1",
|
"pg": "^8.7.1",
|
||||||
"php-serialize": "^4.0.2",
|
"php-serialize": "^4.0.2",
|
||||||
|
"pluralize": "^8.0.0",
|
||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"stripe": "^11.13.0",
|
"stripe": "^11.13.0",
|
||||||
"winston": "^3.7.1",
|
"winston": "^3.7.1",
|
||||||
@@ -126,24 +130,14 @@
|
|||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "^6.4.4",
|
||||||
"@types/pg": "^8.6.1",
|
"@types/pg": "^8.6.1",
|
||||||
"@types/pino": "^7.0.5",
|
"@types/pino": "^7.0.5",
|
||||||
|
"@types/pluralize": "^0.0.30",
|
||||||
"@types/showdown": "^2.0.1",
|
"@types/showdown": "^2.0.1",
|
||||||
"ava": "^3.15.0",
|
"ava": "^5.3.1",
|
||||||
"nodemon": "^2.0.13",
|
"nodemon": "^2.0.13",
|
||||||
"sinon": "^11.1.2",
|
"sinon": "^11.1.2",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"ts-node-dev": "^1.1.8"
|
"ts-node-dev": "^1.1.8"
|
||||||
},
|
},
|
||||||
"ava": {
|
|
||||||
"files": [
|
|
||||||
"test/**/*"
|
|
||||||
],
|
|
||||||
"extensions": [
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"require": [
|
|
||||||
"ts-node/register"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,61 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Get Device Location',
|
||||||
|
key: 'getDeviceLocation',
|
||||||
|
description: 'Get the location of the device.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'MSISDN',
|
||||||
|
key: 'msisdn',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description:
|
||||||
|
'Subscriber number in E.164 format (starting with country code). Optionally prefixed with ' +
|
||||||
|
'.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Latitude',
|
||||||
|
key: 'latitude',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'Latitude component of location',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Longitude',
|
||||||
|
key: 'longitude',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'Longitude component of location',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Accuracy',
|
||||||
|
key: 'accuracy',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'Accuracy expected for location verification in km',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const payload = {
|
||||||
|
ueId: {
|
||||||
|
msisdn: $.step.parameters.msisdn,
|
||||||
|
},
|
||||||
|
latitude: $.step.parameters.latitude,
|
||||||
|
longitude: $.step.parameters.longitude,
|
||||||
|
accuracy: $.step.parameters.accuracy,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await $.http.post(
|
||||||
|
'https://api-eu.vonage.com/camara/location/v0/verify',
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({ raw: response.data });
|
||||||
|
},
|
||||||
|
});
|
3
packages/backend/src/apps/5g-api/actions/index.ts
Normal file
3
packages/backend/src/apps/5g-api/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import getDeviceLocation from './get-device-location';
|
||||||
|
|
||||||
|
export default [getDeviceLocation];
|
4
packages/backend/src/apps/5g-api/assets/favicon.svg
Normal file
4
packages/backend/src/apps/5g-api/assets/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 38" part="logo-svg" fill="#e20074" role="img" aria-labelledby="logo-title-2">
|
||||||
|
<path d="M7.6 25.1H0v-7.6h7.6v7.6ZM0 0v12.9h2.3v-.4c0-6.1 3.4-9.9 9.9-9.9h.4V30c0 3.8-1.5 5.3-5.3 5.3H6.1V38h19.8v-2.7h-1.1c-3.8 0-5.3-1.5-5.3-5.3V2.7h.4c6.5 0 9.9 3.8 9.9 9.9v.4h2.3V0H0Zm24.3 25.1h7.6v-7.6h-7.6v7.6Z">
|
||||||
|
</path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 372 B |
45
packages/backend/src/apps/5g-api/auth/index.ts
Normal file
45
packages/backend/src/apps/5g-api/auth/index.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import verifyCredentials from './verify-credentials';
|
||||||
|
import isStillVerified from './is-still-verified';
|
||||||
|
// import refreshToken from './refresh-token';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'screenName',
|
||||||
|
label: 'Screen Name',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'Screen name of your connection to be used on Automatisch UI.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'privateKey',
|
||||||
|
label: 'Private Key',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'Private Key of your High Mobility OAuth app.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'applicationId',
|
||||||
|
label: 'Application ID',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'App ID of your High Mobility OAuth app.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
verifyCredentials,
|
||||||
|
// isStillVerified,
|
||||||
|
// refreshToken,
|
||||||
|
};
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
|
||||||
|
const isStillVerified = async ($: IGlobalVariable) => {
|
||||||
|
// const user = await getCurrentUser($);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
39
packages/backend/src/apps/5g-api/auth/verify-credentials.ts
Normal file
39
packages/backend/src/apps/5g-api/auth/verify-credentials.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||||
|
const claims = {
|
||||||
|
application_id: $.auth.data.applicationId as string,
|
||||||
|
iat: Math.floor(new Date().getTime() / 1000),
|
||||||
|
exp: Math.floor(new Date().getTime() / 1000) + 24 * 60 * 60,
|
||||||
|
jti: uuidv4(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const privateKey = ($.auth.data.privateKey as string).replaceAll('\\n', '\n');
|
||||||
|
|
||||||
|
const jwtToken = jwt.sign(claims, privateKey, {
|
||||||
|
algorithm: 'RS256',
|
||||||
|
});
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Authorization: 'Bearer ' + jwtToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await $.http.post(
|
||||||
|
'https://api.nexmo.com/oauth2/token?grant_type=client_credentials',
|
||||||
|
null,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData = Object.fromEntries(new URLSearchParams(response.data));
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accessToken: responseData.access_token,
|
||||||
|
tokenType: responseData.token_type,
|
||||||
|
expiresIn: responseData.expires_in,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
13
packages/backend/src/apps/5g-api/common/add-auth-header.ts
Normal file
13
packages/backend/src/apps/5g-api/common/add-auth-header.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { TBeforeRequest } from '@automatisch/types';
|
||||||
|
|
||||||
|
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||||
|
const { accessToken } = $.auth.data;
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
requestConfig.headers.Authorization = `Bearer ${accessToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addAuthHeader;
|
0
packages/backend/src/apps/5g-api/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/5g-api/index.d.ts
vendored
Normal file
18
packages/backend/src/apps/5g-api/index.ts
Normal file
18
packages/backend/src/apps/5g-api/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import defineApp from '../../helpers/define-app';
|
||||||
|
import addAuthHeader from './common/add-auth-header';
|
||||||
|
import auth from './auth';
|
||||||
|
import actions from './actions';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: '5G API',
|
||||||
|
key: '5g-api',
|
||||||
|
iconUrl: '{BASE_URL}/apps/5g-api/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/5g-api/connection',
|
||||||
|
supportsConnections: true,
|
||||||
|
baseUrl: 'https://developer.telekom.de',
|
||||||
|
apiBaseUrl: 'https://api.developer.telekom.de',
|
||||||
|
primaryColor: 'e20074',
|
||||||
|
beforeRequest: [addAuthHeader],
|
||||||
|
auth,
|
||||||
|
actions,
|
||||||
|
});
|
@@ -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;
|
@@ -1,3 +1,5 @@
|
|||||||
import text from './text';
|
import text from './text';
|
||||||
|
import numbers from './numbers';
|
||||||
|
import dateTime from './date-time';
|
||||||
|
|
||||||
export default [text];
|
export default [text, numbers, dateTime];
|
||||||
|
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;
|
@@ -1,18 +1,27 @@
|
|||||||
import defineAction from '../../../../helpers/define-action';
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
import capitalize from './transformers/capitalize';
|
import capitalize from './transformers/capitalize';
|
||||||
import htmlToMarkdown from './transformers/html-to-markdown';
|
|
||||||
import markdownToHtml from './transformers/markdown-to-html';
|
|
||||||
import useDefaultValue from './transformers/use-default-value';
|
|
||||||
import extractEmailAddress from './transformers/extract-email-address';
|
import extractEmailAddress from './transformers/extract-email-address';
|
||||||
import extractNumber from './transformers/extract-number';
|
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 = {
|
const transformers = {
|
||||||
capitalize,
|
capitalize,
|
||||||
htmlToMarkdown,
|
|
||||||
markdownToHtml,
|
|
||||||
useDefaultValue,
|
|
||||||
extractEmailAddress,
|
extractEmailAddress,
|
||||||
extractNumber,
|
extractNumber,
|
||||||
|
htmlToMarkdown,
|
||||||
|
lowercase,
|
||||||
|
markdownToHtml,
|
||||||
|
pluralize,
|
||||||
|
replace,
|
||||||
|
trimWhitespace,
|
||||||
|
useDefaultValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineAction({
|
export default defineAction({
|
||||||
@@ -26,15 +35,18 @@ export default defineAction({
|
|||||||
key: 'transform',
|
key: 'transform',
|
||||||
type: 'dropdown' as const,
|
type: 'dropdown' as const,
|
||||||
required: true,
|
required: true,
|
||||||
description: 'Pick a channel to send the message to.',
|
|
||||||
variables: true,
|
variables: true,
|
||||||
options: [
|
options: [
|
||||||
{ label: 'Capitalize', value: 'capitalize' },
|
{ label: 'Capitalize', value: 'capitalize' },
|
||||||
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
|
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
|
||||||
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
|
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
|
||||||
{ label: 'Use Default Value', value: 'useDefaultValue' },
|
|
||||||
{ label: 'Extract Email Address', value: 'extractEmailAddress' },
|
{ label: 'Extract Email Address', value: 'extractEmailAddress' },
|
||||||
{ label: 'Extract Number', value: 'extractNumber' },
|
{ 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: {
|
additionalFields: {
|
||||||
type: 'query',
|
type: 'query',
|
||||||
|
@@ -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,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,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;
|
@@ -1,18 +1,34 @@
|
|||||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||||
import capitalize from './options/capitalize';
|
import capitalize from './text/capitalize';
|
||||||
import htmlToMarkdown from './options/html-to-markdown';
|
import extractEmailAddress from './text/extract-email-address';
|
||||||
import markdownToHtml from './options/markdown-to-html';
|
import extractNumber from './text/extract-number';
|
||||||
import useDefaultValue from './options/use-default-value';
|
import htmlToMarkdown from './text/html-to-markdown';
|
||||||
import extractEmailAddress from './options/extract-email-address';
|
import lowercase from './text/lowercase';
|
||||||
import extractNumber from './options/extract-number';
|
import markdownToHtml from './text/markdown-to-html';
|
||||||
|
import pluralize from './text/pluralize';
|
||||||
|
import replace from './text/replace';
|
||||||
|
import trimWhitespace from './text/trim-whitespace';
|
||||||
|
import useDefaultValue from './text/use-default-value';
|
||||||
|
import performMathOperation from './numbers/perform-math-operation';
|
||||||
|
import randomNumber from './numbers/random-number';
|
||||||
|
import formatNumber from './numbers/format-number';
|
||||||
|
import formatDateTime from './date-time/format-date-time';
|
||||||
|
|
||||||
const options: IJSONObject = {
|
const options: IJSONObject = {
|
||||||
capitalize,
|
capitalize,
|
||||||
htmlToMarkdown,
|
|
||||||
markdownToHtml,
|
|
||||||
useDefaultValue,
|
|
||||||
extractEmailAddress,
|
extractEmailAddress,
|
||||||
extractNumber,
|
extractNumber,
|
||||||
|
htmlToMarkdown,
|
||||||
|
lowercase,
|
||||||
|
markdownToHtml,
|
||||||
|
pluralize,
|
||||||
|
replace,
|
||||||
|
trimWhitespace,
|
||||||
|
useDefaultValue,
|
||||||
|
performMathOperation,
|
||||||
|
randomNumber,
|
||||||
|
formatNumber,
|
||||||
|
formatDateTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@@ -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 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 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,20 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Get Battery Level',
|
||||||
|
key: 'getBatteryLevel',
|
||||||
|
description: 'Get the battery level of a vehicle',
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const response = await $.http.get(
|
||||||
|
`https://sandbox.rest-api.high-mobility.com/v5/charging`
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: {
|
||||||
|
batteryLevel: response.data.batteryLevel.data,
|
||||||
|
estimatedRange: response.data.estimatedRange.data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,15 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Get Vehicle Location',
|
||||||
|
key: 'getVehicleLocation',
|
||||||
|
description: 'Get the location of a vehicle',
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const response = await $.http.get(
|
||||||
|
`https://sandbox.rest-api.high-mobility.com/v5/vehicle_location`
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({ raw: response.data });
|
||||||
|
},
|
||||||
|
});
|
4
packages/backend/src/apps/high-mobility/actions/index.ts
Normal file
4
packages/backend/src/apps/high-mobility/actions/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import getVehicleLocation from './get-vehicle-location';
|
||||||
|
import getBatteryLevel from './get-battery-level';
|
||||||
|
|
||||||
|
export default [getVehicleLocation, getBatteryLevel];
|
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1098 0H21.9778C27.5162 0 32.0436 4.52747 31.9997 10.0659V21.9341C31.9997 27.4725 27.4723 32 21.9338 32H10.0658C4.52743 32 0 27.4725 0 21.9341V10.0659C0 4.52747 4.52743 0 10.1098 0ZM9.58232 17.1868H14.4174C14.9889 17.1868 15.4724 16.7033 15.4724 16.1319C15.4724 15.5604 14.9889 15.0769 14.4174 15.0769H9.58232V10.4615C9.58232 9.89011 9.09881 9.40659 8.52739 9.40659C7.95597 9.40659 7.47245 9.89011 7.47245 10.4615V21.5824C7.47245 22.1538 7.95597 22.6374 8.52739 22.6374C9.09881 22.6374 9.58232 22.1538 9.58232 21.5824V17.1868ZM23.5163 22.6374C24.0877 22.6374 24.5712 22.1538 24.5712 21.5824H24.5674V10.4691C24.5712 10.4336 24.5731 10.3975 24.5731 10.361C24.5731 9.80393 24.1216 9.35237 23.5645 9.35237C23.2507 9.35237 22.9704 9.49569 22.7854 9.72044L22.7851 9.7192L17.4943 14.4615C17.0548 14.8571 17.0108 15.5165 17.4064 15.956C17.6262 16.1758 17.8899 16.3077 18.1976 16.3077C18.4614 16.3077 18.6811 16.2198 18.9009 16.044L22.4613 12.8352V21.5824C22.4613 22.1538 22.9448 22.6374 23.5163 22.6374Z" fill="#0A4B58"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
import { IField, IGlobalVariable } from '@automatisch/types';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
export default async function generateAuthUrl($: IGlobalVariable) {
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value as string;
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
response_type: 'code',
|
||||||
|
client_id: $.auth.data.clientId as string,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `https://sandbox.owner-panel.high-mobility.com/oauth/new?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
}
|
93
packages/backend/src/apps/high-mobility/auth/index.ts
Normal file
93
packages/backend/src/apps/high-mobility/auth/index.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
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/high-mobility/connections/add',
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'When asked to input an OAuth callback or redirect URL in High Mobility OAuth, enter the URL above.',
|
||||||
|
clickToCopy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'screenName',
|
||||||
|
label: 'Screen Name',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'Screen name of your connection to be used on Automatisch UI.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientId',
|
||||||
|
label: 'Client ID',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'Client ID of your High Mobility OAuth app.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientSecret',
|
||||||
|
label: 'Client Secret',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'Client Secret of your High Mobility OAuth app.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'privateKey',
|
||||||
|
label: 'Private Key',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'Private Key of your High Mobility OAuth app.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'appId',
|
||||||
|
label: 'App ID',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'App ID of your High Mobility OAuth app.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientSerialNumber',
|
||||||
|
label: 'Client Serial Number',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'Client Serial Number of your High Mobility OAuth app.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
generateAuthUrl,
|
||||||
|
verifyCredentials,
|
||||||
|
isStillVerified,
|
||||||
|
refreshToken,
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
import getVehicleInfo from '../common/get-vehicle-info';
|
||||||
|
|
||||||
|
const isStillVerified = async ($: IGlobalVariable) => {
|
||||||
|
const user = await getVehicleInfo($);
|
||||||
|
return !!user;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
@@ -0,0 +1,27 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
|
||||||
|
const refreshToken = async ($: IGlobalVariable) => {
|
||||||
|
const payload = {
|
||||||
|
client_id: $.auth.data.clientId as string,
|
||||||
|
client_secret: $.auth.data.clientSecret as string,
|
||||||
|
refresh_token: $.auth.data.refreshToken as string,
|
||||||
|
grant_type: 'refresh_token',
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await $.http.post(
|
||||||
|
'https://sandbox.api.high-mobility.com/v1/access_tokens',
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
tokenType: data.token_type,
|
||||||
|
status: data.status,
|
||||||
|
scope: data.scope,
|
||||||
|
refreshToken: data.refresh_token,
|
||||||
|
expiresIn: data.expires_in,
|
||||||
|
accessToken: data.access_token,
|
||||||
|
authorizationId: data.authorization_id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default refreshToken;
|
@@ -0,0 +1,36 @@
|
|||||||
|
import { IGlobalVariable, IField } from '@automatisch/types';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value as string;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
client_id: $.auth.data.clientId as string,
|
||||||
|
client_secret: $.auth.data.clientSecret as string,
|
||||||
|
code: $.auth.data.code as string,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await $.http.post(
|
||||||
|
'https://sandbox.api.high-mobility.com/v1/access_tokens',
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData = Object.fromEntries(new URLSearchParams(response.data));
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
tokenType: responseData.token_type,
|
||||||
|
status: responseData.status,
|
||||||
|
scope: responseData.scope,
|
||||||
|
refreshToken: responseData.refresh_token,
|
||||||
|
expiresIn: responseData.expires_in,
|
||||||
|
accessToken: responseData.access_token,
|
||||||
|
authorizationId: responseData.authorization_id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
@@ -0,0 +1,41 @@
|
|||||||
|
import { TBeforeRequest } from '@automatisch/types';
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||||
|
const { accessToken } = $.auth.data;
|
||||||
|
const { url } = requestConfig;
|
||||||
|
|
||||||
|
if (accessToken && url === '/v1/vehicleinfo') {
|
||||||
|
requestConfig.headers.Authorization = `Bearer ${accessToken}`;
|
||||||
|
return requestConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
version: '3.0',
|
||||||
|
type: 'rest_api',
|
||||||
|
private_key: ($.auth.data.privateKey as string).replaceAll('\\n', '\n'),
|
||||||
|
app_uri: 'https://sandbox.rest-api.high-mobility.com/v5',
|
||||||
|
app_id: $.auth.data.appId as string,
|
||||||
|
client_serial_number: $.auth.data.clientSerialNumber as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
ver: config.version,
|
||||||
|
aud: config.app_uri,
|
||||||
|
iss: config.client_serial_number,
|
||||||
|
iat: Math.round(Date.now() / 1000),
|
||||||
|
jti: uuidv4(),
|
||||||
|
sub: $.auth.data.accessToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
const priv = Buffer.from(config.private_key, 'utf8');
|
||||||
|
|
||||||
|
const token = jwt.sign(payload, priv, { algorithm: 'ES256' });
|
||||||
|
|
||||||
|
requestConfig.headers.Authorization = `Bearer ${token}`;
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addAuthHeader;
|
@@ -0,0 +1,13 @@
|
|||||||
|
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
const getVehicleInfo = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
||||||
|
const response = await $.http.get(
|
||||||
|
'https://sandbox.api.high-mobility.com/v1/vehicleinfo'
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentVehicle = response.data;
|
||||||
|
|
||||||
|
return currentVehicle;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getVehicleInfo;
|
0
packages/backend/src/apps/high-mobility/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/high-mobility/index.d.ts
vendored
Normal file
18
packages/backend/src/apps/high-mobility/index.ts
Normal file
18
packages/backend/src/apps/high-mobility/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import defineApp from '../../helpers/define-app';
|
||||||
|
import addAuthHeader from './common/add-auth-header';
|
||||||
|
import auth from './auth';
|
||||||
|
import actions from './actions';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'High Mobility',
|
||||||
|
key: 'high-mobility',
|
||||||
|
iconUrl: '{BASE_URL}/apps/high-mobility/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/high-mobility/connection',
|
||||||
|
supportsConnections: true,
|
||||||
|
baseUrl: 'https://high-mobility.com',
|
||||||
|
apiBaseUrl: 'https://api.high-mobility.com',
|
||||||
|
primaryColor: '000000',
|
||||||
|
beforeRequest: [addAuthHeader],
|
||||||
|
auth,
|
||||||
|
actions,
|
||||||
|
});
|
@@ -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,
|
||||||
|
});
|
3
packages/backend/src/apps/positionstack/actions/index.ts
Normal file
3
packages/backend/src/apps/positionstack/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import reverseGeocoding from './reverse-geocoding';
|
||||||
|
|
||||||
|
export default [reverseGeocoding];
|
@@ -0,0 +1,35 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Reverse Geocoding',
|
||||||
|
key: 'reverseGeocoding',
|
||||||
|
description: 'Get the address of a location from its longitude and latitude.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Latitude',
|
||||||
|
key: 'latitude',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'Longitude of the location.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Longitude',
|
||||||
|
key: 'longitude',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'Latitude of the location.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const response = await $.http.get(
|
||||||
|
`/reverse?access_key=${$.auth.data.apiKey}&query=${$.step.parameters.longitude},${$.step.parameters.latitude}`
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: response.data.data[0],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 21C15.5 17.4 19 14.1764 19 10.2C19 6.22355 15.866 3 12 3C8.13401 3 5 6.22355 5 10.2C5 14.1764 8.5 17.4 12 21Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 12C13.1046 12 14 11.1046 14 10C14 8.89543 13.1046 8 12 8C10.8954 8 10 8.89543 10 10C10 11.1046 10.8954 12 12 12Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 526 B |
33
packages/backend/src/apps/positionstack/auth/index.ts
Normal file
33
packages/backend/src/apps/positionstack/auth/index.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import verifyCredentials from './verify-credentials';
|
||||||
|
import isStillVerified from './is-still-verified';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'screenName',
|
||||||
|
label: 'Screen Name',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'Screen name of your connection to be used on Automatisch UI.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apiKey',
|
||||||
|
label: 'API Key',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'Positionstack API Key of your account.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
verifyCredentials,
|
||||||
|
isStillVerified,
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
import verifyCredentials from './verify-credentials';
|
||||||
|
|
||||||
|
const isStillVerified = async ($: IGlobalVariable) => {
|
||||||
|
await verifyCredentials($);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
@@ -0,0 +1,13 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
|
||||||
|
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||||
|
await $.http.get(
|
||||||
|
`/reverse?access_key=${$.auth.data.apiKey}&query=50.94852,6.944772`
|
||||||
|
);
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
screenName: $.auth.data.screenName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
0
packages/backend/src/apps/positionstack/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/positionstack/index.d.ts
vendored
Normal file
16
packages/backend/src/apps/positionstack/index.ts
Normal file
16
packages/backend/src/apps/positionstack/index.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import defineApp from '../../helpers/define-app';
|
||||||
|
import auth from './auth';
|
||||||
|
import actions from './actions';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'Positionstack',
|
||||||
|
key: 'positionstack',
|
||||||
|
iconUrl: '{BASE_URL}/apps/positionstack/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/positionstack/connection',
|
||||||
|
supportsConnections: true,
|
||||||
|
baseUrl: 'https://positionstack.com',
|
||||||
|
apiBaseUrl: 'http://api.positionstack.com/v1',
|
||||||
|
primaryColor: '0d2d45',
|
||||||
|
auth,
|
||||||
|
actions,
|
||||||
|
});
|
@@ -0,0 +1,36 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Get EV Battery Level',
|
||||||
|
key: 'getEvBatteryLevel',
|
||||||
|
description: 'Get the battery level of an electric vehicle.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Vehicle',
|
||||||
|
key: 'vehicle',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'The vehicle to get the location of.',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listVehicles',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const { vehicle } = $.step.parameters;
|
||||||
|
const response = await $.http.get(
|
||||||
|
`/vehicles/${vehicle}/battery?mode=simulated`
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({ raw: response.data });
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,36 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Get Vehicle Location',
|
||||||
|
key: 'getVehicleLocation',
|
||||||
|
description: 'Get the location of a vehicle',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Vehicle',
|
||||||
|
key: 'vehicle',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'The vehicle to get the location of.',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listVehicles',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const { vehicle } = $.step.parameters;
|
||||||
|
const response = await $.http.get(
|
||||||
|
`/vehicles/${vehicle}/location?mode=simulated`
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({ raw: response.data });
|
||||||
|
},
|
||||||
|
});
|
4
packages/backend/src/apps/smartcar/actions/index.ts
Normal file
4
packages/backend/src/apps/smartcar/actions/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import getVehicleLocation from './get-vehicle-location';
|
||||||
|
import getEvBatteryLevel from './get-ev-battery-level';
|
||||||
|
|
||||||
|
export default [getVehicleLocation, getEvBatteryLevel];
|
11
packages/backend/src/apps/smartcar/assets/favicon.svg
Normal file
11
packages/backend/src/apps/smartcar/assets/favicon.svg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<svg width="217" height="60" viewBox="0 0 217 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M30 0C13.4328 0 0 13.4328 0 30C0 46.5672 13.4328 60 30 60C46.5672 60 60 46.5672 60 30C60 13.4328 46.5672 0 30 0ZM30 7.5C39.7625 7.5 48.0107 13.7875 51.123 22.5H8.877C11.9893 13.7875 20.2375 7.5 30 7.5ZM30 33.75C29.2583 33.75 28.5333 33.5301 27.9166 33.118C27.2999 32.706 26.8193 32.1203 26.5355 31.4351C26.2516 30.7498 26.1774 29.9958 26.3221 29.2684C26.4668 28.541 26.8239 27.8728 27.3483 27.3483C27.8728 26.8239 28.541 26.4668 29.2684 26.3221C29.9958 26.1774 30.7498 26.2516 31.4351 26.5355C32.1203 26.8193 32.706 27.2999 33.118 27.9166C33.5301 28.5333 33.75 29.2583 33.75 30C33.7503 30.4925 33.6536 30.9803 33.4652 31.4355C33.2769 31.8906 33.0007 32.3041 32.6524 32.6524C32.3041 33.0007 31.8906 33.2769 31.4355 33.4652C30.9803 33.6536 30.4925 33.7503 30 33.75ZM7.5 30C17.7467 30 26.0448 39.873 26.2206 52.119C15.615 50.3027 7.5 41.1107 7.5 30ZM33.7794 52.119C33.9552 39.873 42.2533 30 52.5 30C52.5 41.1107 44.385 50.3027 33.7794 52.119Z" fill="black"/>
|
||||||
|
<path d="M79.8724 40.5406C78.2018 40.5406 76.7369 40.2739 75.4776 39.7405C74.2182 39.184 73.1131 38.3608 72.1622 37.2709L75.015 34.7665C75.6575 35.5781 76.3771 36.1926 77.1738 36.61C77.9705 37.0274 78.8958 37.2361 79.9495 37.2361C81.9028 37.2361 82.8794 36.4941 82.8794 35.01C82.8794 34.4303 82.7123 33.9781 82.3782 33.6535C82.0698 33.3056 81.5301 33.0737 80.7591 32.9578L78.8701 32.6447C76.7626 32.2969 75.2334 31.7056 74.2825 30.8708C73.3316 30.036 72.8561 28.8186 72.8561 27.2185C72.8561 25.3171 73.4729 23.9025 74.7065 22.975C75.9402 22.0242 77.6878 21.5489 79.9495 21.5489C81.5172 21.5489 82.8665 21.7808 83.9974 22.2445C85.1539 22.7083 86.1562 23.3924 87.0044 24.2968L84.2672 26.7664C83.7532 26.1866 83.1235 25.7229 82.3782 25.375C81.6586 25.0272 80.849 24.8533 79.9495 24.8533C78.1504 24.8533 77.2509 25.5837 77.2509 27.0446C77.2509 27.6475 77.4308 28.1113 77.7906 28.436C78.1504 28.7374 78.7159 28.9461 79.4869 29.0621L81.4144 29.3751C83.5219 29.7229 85.0254 30.3143 85.9249 31.1491C86.8502 31.9607 87.3128 33.1085 87.3128 34.5926C87.3128 36.4709 86.6702 37.9318 85.3852 38.9753C84.1002 40.0188 82.2626 40.5406 79.8724 40.5406Z" fill="black"/>
|
||||||
|
<path d="M91.0992 40.1232V31.0447V21.9663H95.7639V24.9924H96.3036C97.1003 22.6967 98.6809 21.5489 101.045 21.5489C103.564 21.5489 105.196 22.7431 105.941 25.1315H106.404C106.841 23.9025 107.509 22.9982 108.409 22.4185C109.334 21.8387 110.452 21.5489 111.763 21.5489C113.459 21.5489 114.782 22.1054 115.733 23.2185C116.684 24.3083 117.16 25.8736 117.16 27.9142V40.1232H112.495V28.6447C112.495 26.2794 111.557 25.0968 109.681 25.0968C108.781 25.0968 108.01 25.3518 107.368 25.862C106.751 26.349 106.443 27.0446 106.443 27.949V40.1232H101.778V28.6447C101.778 27.3925 101.547 26.4881 101.084 25.9316C100.621 25.375 99.9274 25.0968 99.0022 25.0968C98.1027 25.0968 97.3316 25.3518 96.6891 25.862C96.0723 26.349 95.7639 27.0446 95.7639 27.949V40.1232H91.0992Z" fill="black"/>
|
||||||
|
<path d="M135.115 40.1232C133.034 40.1232 131.941 39.0912 131.838 37.0274H131.376C131.119 38.1405 130.541 39.0101 129.641 39.6362C128.741 40.2391 127.624 40.5406 126.287 40.5406C124.437 40.5406 123.049 40.0768 122.124 39.1492C121.198 38.2217 120.736 36.9231 120.736 35.2535C120.736 33.352 121.391 31.9375 122.702 31.0099C124.013 30.0824 125.902 29.6186 128.369 29.6186H131.491V28.0533C131.491 26.9867 131.234 26.1982 130.72 25.6881C130.232 25.1779 129.41 24.9228 128.253 24.9228C127.302 24.9228 126.506 25.1315 125.863 25.5489C125.22 25.9432 124.655 26.5113 124.167 27.2533L121.43 25.0272C122.175 23.9837 123.113 23.1489 124.244 22.5228C125.4 21.8735 126.865 21.5489 128.639 21.5489C133.65 21.5489 136.156 23.6822 136.156 27.949V36.7492H137.93V40.1232H135.115ZM128.06 37.4448C129.011 37.4448 129.821 37.2014 130.489 36.7144C131.157 36.2042 131.491 35.5086 131.491 34.6274V32.2273H128.677C126.518 32.2273 125.439 32.981 125.439 34.4883V35.2535C125.439 36.0187 125.67 36.5753 126.133 36.9231C126.595 37.2709 127.238 37.4448 128.06 37.4448Z" fill="black"/>
|
||||||
|
<path d="M141.547 40.1232V21.9663H146.212V25.6881H146.751C146.983 24.4823 147.458 23.5663 148.178 22.9402C148.923 22.2909 149.887 21.9663 151.069 21.9663H152.071V26.2794H150.529C149.064 26.2794 147.972 26.4881 147.253 26.9055C146.559 27.3229 146.212 28.0533 146.212 29.0968V40.1232H141.547Z" fill="black"/>
|
||||||
|
<path d="M161.367 40.1232C159.748 40.1232 158.578 39.7985 157.859 39.1492C157.139 38.4999 156.779 37.468 156.779 36.0535V25.3402H154.119V21.9663H155.507C156.175 21.9663 156.625 21.8271 156.856 21.5489C157.113 21.2706 157.242 20.83 157.242 20.2271V17.027H161.444V21.9663H165.068V25.3402H161.444V36.7492H164.759V40.1232H161.367Z" fill="black"/>
|
||||||
|
<path d="M175.672 40.5406C172.948 40.5406 170.866 39.7753 169.427 38.2449C168.013 36.7144 167.307 34.3143 167.307 31.0447C167.307 27.7751 168.013 25.375 169.427 23.8446C170.866 22.3141 172.948 21.5489 175.672 21.5489C177.651 21.5489 179.219 21.9547 180.376 22.7663C181.558 23.5779 182.406 24.691 182.92 26.1055L179.103 27.6359C178.898 26.7548 178.525 26.0939 177.985 25.6533C177.471 25.1895 176.7 24.9576 175.672 24.9576C173.359 24.9576 172.203 26.2098 172.203 28.7142V33.3752C172.203 35.8796 173.359 37.1318 175.672 37.1318C176.752 37.1318 177.574 36.8999 178.14 36.4361C178.705 35.9723 179.129 35.2535 179.412 34.2796L183.036 35.8448C181.853 38.9753 179.399 40.5406 175.672 40.5406Z" fill="black"/>
|
||||||
|
<path d="M199.53 40.1232C197.449 40.1232 196.356 39.0912 196.253 37.0274H195.791C195.534 38.1405 194.956 39.0101 194.056 39.6362C193.157 40.2391 192.039 40.5406 190.702 40.5406C188.852 40.5406 187.464 40.0768 186.539 39.1492C185.613 38.2217 185.151 36.9231 185.151 35.2535C185.151 33.352 185.806 31.9375 187.117 31.0099C188.428 30.0824 190.317 29.6186 192.784 29.6186H195.907V28.0533C195.907 26.9867 195.649 26.1982 195.135 25.6881C194.647 25.1779 193.825 24.9228 192.668 24.9228C191.717 24.9228 190.921 25.1315 190.278 25.5489C189.636 25.9432 189.07 26.5113 188.582 27.2533L185.845 25.0272C186.59 23.9837 187.528 23.1489 188.659 22.5228C189.815 21.8735 191.28 21.5489 193.054 21.5489C198.065 21.5489 200.571 23.6822 200.571 27.949V36.7492H202.345V40.1232H199.53ZM192.475 37.4448C193.426 37.4448 194.236 37.2014 194.904 36.7144C195.572 36.2042 195.907 35.5086 195.907 34.6274V32.2273H193.092C190.933 32.2273 189.854 32.981 189.854 34.4883V35.2535C189.854 36.0187 190.085 36.5753 190.548 36.9231C191.011 37.2709 191.653 37.4448 192.475 37.4448Z" fill="black"/>
|
||||||
|
<path d="M205.962 40.1232V21.9663H210.627V25.6881H211.166C211.398 24.4823 211.873 23.5663 212.593 22.9402C213.338 22.2909 214.302 21.9663 215.484 21.9663H216.486V26.2794H214.944C213.48 26.2794 212.387 26.4881 211.668 26.9055C210.974 27.3229 210.627 28.0533 210.627 29.0968V40.1232H205.962Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 6.6 KiB |
30
packages/backend/src/apps/smartcar/auth/generate-auth-url.ts
Normal file
30
packages/backend/src/apps/smartcar/auth/generate-auth-url.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { IField, IGlobalVariable } from '@automatisch/types';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
export default async function generateAuthUrl($: IGlobalVariable) {
|
||||||
|
const scopes = [
|
||||||
|
'read_odometer',
|
||||||
|
'required:read_vehicle_info',
|
||||||
|
'required:read_location',
|
||||||
|
'required:read_battery',
|
||||||
|
];
|
||||||
|
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value as string;
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
response_type: 'code',
|
||||||
|
client_id: $.auth.data.clientId as string,
|
||||||
|
scope: scopes.join(' '),
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
mode: 'simulated',
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `https://connect.smartcar.com/oauth/authorize?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
}
|
58
packages/backend/src/apps/smartcar/auth/index.ts
Normal file
58
packages/backend/src/apps/smartcar/auth/index.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import generateAuthUrl from './generate-auth-url';
|
||||||
|
import verifyCredentials from './verify-credentials';
|
||||||
|
import isStillVerified from './is-still-verified';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'oAuthRedirectUrl',
|
||||||
|
label: 'OAuth Redirect URL',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: true,
|
||||||
|
value: '{WEB_APP_URL}/app/twitter/connections/add',
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'When asked to input an OAuth callback or redirect URL in Smartcar OAuth, enter the URL above.',
|
||||||
|
clickToCopy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'screenName',
|
||||||
|
label: 'Screen Name',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'Screen name of your connection to be used on Automatisch UI.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '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,
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
import getCurrentUser from '../common/get-current-user';
|
||||||
|
|
||||||
|
const isStillVerified = async ($: IGlobalVariable) => {
|
||||||
|
const user = await getCurrentUser($);
|
||||||
|
return !!user;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
@@ -0,0 +1,41 @@
|
|||||||
|
import { IGlobalVariable, IField } from '@automatisch/types';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||||
|
const token = Buffer.from(
|
||||||
|
`${$.auth.data.clientId}:${$.auth.data.clientSecret}`
|
||||||
|
).toString('base64');
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Basic ${token}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value as string;
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
code: $.auth.data.code as string,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await $.http.post(
|
||||||
|
`https://auth.smartcar.com/oauth/token`,
|
||||||
|
params.toString(),
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData = Object.fromEntries(new URLSearchParams(response.data));
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accessToken: responseData.access_token,
|
||||||
|
tokenType: responseData.token_type,
|
||||||
|
expiresIn: responseData.expires_in,
|
||||||
|
refreshToken: responseData.refresh_token,
|
||||||
|
screenName: $.auth.data.screenName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
13
packages/backend/src/apps/smartcar/common/add-auth-header.ts
Normal file
13
packages/backend/src/apps/smartcar/common/add-auth-header.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { TBeforeRequest } from '@automatisch/types';
|
||||||
|
|
||||||
|
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||||
|
const { accessToken } = $.auth.data;
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
requestConfig.headers.Authorization = `Bearer ${accessToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addAuthHeader;
|
@@ -0,0 +1,10 @@
|
|||||||
|
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
const getCurrentUser = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
||||||
|
const response = await $.http.get('/user');
|
||||||
|
const currentUser = response.data;
|
||||||
|
|
||||||
|
return currentUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCurrentUser;
|
3
packages/backend/src/apps/smartcar/dynamic-data/index.ts
Normal file
3
packages/backend/src/apps/smartcar/dynamic-data/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import listVehicles from './list-vehicles';
|
||||||
|
|
||||||
|
export default [listVehicles];
|
@@ -0,0 +1,31 @@
|
|||||||
|
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'List vehicles',
|
||||||
|
key: 'listVehicles',
|
||||||
|
|
||||||
|
async run($: IGlobalVariable) {
|
||||||
|
const vehicles: {
|
||||||
|
data: IJSONObject[];
|
||||||
|
error: IJSONObject | null;
|
||||||
|
} = {
|
||||||
|
data: [],
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response: any = await $.http.get('/vehicles');
|
||||||
|
|
||||||
|
for (const vehicle of response.data.vehicles) {
|
||||||
|
const response: any = await $.http.get(`/vehicles/${vehicle}`);
|
||||||
|
|
||||||
|
const vehicleName = `${response.data.make} - ${response.data.model} (${response.data.year})`;
|
||||||
|
|
||||||
|
vehicles.data.push({
|
||||||
|
value: vehicle as string,
|
||||||
|
name: vehicleName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return vehicles;
|
||||||
|
},
|
||||||
|
};
|
0
packages/backend/src/apps/smartcar/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/smartcar/index.d.ts
vendored
Normal file
20
packages/backend/src/apps/smartcar/index.ts
Normal file
20
packages/backend/src/apps/smartcar/index.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import defineApp from '../../helpers/define-app';
|
||||||
|
import addAuthHeader from './common/add-auth-header';
|
||||||
|
import auth from './auth';
|
||||||
|
import actions from './actions';
|
||||||
|
import dynamicData from './dynamic-data';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'Smartcar',
|
||||||
|
key: 'smartcar',
|
||||||
|
iconUrl: '{BASE_URL}/apps/smartcar/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/smartcar/connection',
|
||||||
|
supportsConnections: true,
|
||||||
|
baseUrl: 'https://smartcar.com',
|
||||||
|
apiBaseUrl: 'https://api.smartcar.com/v2.0',
|
||||||
|
primaryColor: '000000',
|
||||||
|
beforeRequest: [addAuthHeader],
|
||||||
|
auth,
|
||||||
|
actions,
|
||||||
|
dynamicData,
|
||||||
|
});
|
3
packages/backend/src/apps/vonage/actions/index.ts
Normal file
3
packages/backend/src/apps/vonage/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import sendMessage from './send-message';
|
||||||
|
|
||||||
|
export default [sendMessage];
|
@@ -0,0 +1,82 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Send Message',
|
||||||
|
key: 'sendMessage',
|
||||||
|
description: 'Send a message to a number.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Message Type',
|
||||||
|
key: 'messageType',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'The type of message to send. e.g. text',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Channel',
|
||||||
|
key: 'channel',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description:
|
||||||
|
'The channel to send the message through. e.g. sms, whatsapp',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'From Number',
|
||||||
|
key: 'fromNumber',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description:
|
||||||
|
'The number to send the message from. Include country code. Example: 15551234567',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'To Number',
|
||||||
|
key: 'toNumber',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description:
|
||||||
|
'The number to send the message to. Include country code. Example: 15551234567',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Message',
|
||||||
|
key: 'message',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'The message to send.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const messageType = $.step.parameters.messageType as string;
|
||||||
|
const channel = $.step.parameters.channel as string;
|
||||||
|
const messageBody = $.step.parameters.message as string;
|
||||||
|
const fromNumber = ($.step.parameters.fromNumber as string).trim();
|
||||||
|
const toNumber = ($.step.parameters.toNumber as string).trim();
|
||||||
|
|
||||||
|
const basicAuthToken = Buffer.from(
|
||||||
|
`${$.auth.data.apiKey}:${$.auth.data.apiSecret}`
|
||||||
|
).toString('base64');
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Basic ${basicAuthToken}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
message_type: messageType,
|
||||||
|
text: messageBody,
|
||||||
|
to: toNumber,
|
||||||
|
from: fromNumber,
|
||||||
|
channel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await $.http.post('/messages', payload, { headers });
|
||||||
|
|
||||||
|
$.setActionItem({ raw: response.data });
|
||||||
|
},
|
||||||
|
});
|
4
packages/backend/src/apps/vonage/assets/favicon.svg
Normal file
4
packages/backend/src/apps/vonage/assets/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg viewBox="0 0 230 200" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="currentColor" d="M45.3408,0 L-0.0002,0 L64.6808,146.958 C65.1748,148.081 66.7718,148.07 67.2508,146.942 L88.7628,96.337 L45.3408,0 Z"></path>
|
||||||
|
<path fill="currentColor" d="M183.4502,0 C183.4502,0 113.9562,159.156 104.6482,173.833 C93.8292,190.896 86.6592,197.409 73.3912,199.496 C73.2682,199.515 73.1772,199.621 73.1772,199.746 C73.1772,199.886 73.2912,200 73.4312,200 L114.9552,200 C132.9432,200 145.9152,184.979 153.1042,171.714 C161.2742,156.637 229.5902,0 229.5902,0 L183.4502,0 Z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 593 B |
44
packages/backend/src/apps/vonage/auth/index.ts
Normal file
44
packages/backend/src/apps/vonage/auth/index.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import verifyCredentials from './verify-credentials';
|
||||||
|
import isStillVerified from './is-still-verified';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'screenName',
|
||||||
|
label: 'Screen Name',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'Screen name of your connection to be used on Automatisch UI.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apiKey',
|
||||||
|
label: 'API Key',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'API Key from Vonage.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apiSecret',
|
||||||
|
label: 'API Secret',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'API Secret from Vonage.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
verifyCredentials,
|
||||||
|
isStillVerified,
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
import verifyCredentials from './verify-credentials';
|
||||||
|
|
||||||
|
const isStillVerified = async ($: IGlobalVariable) => {
|
||||||
|
await verifyCredentials($);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
13
packages/backend/src/apps/vonage/auth/verify-credentials.ts
Normal file
13
packages/backend/src/apps/vonage/auth/verify-credentials.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
|
||||||
|
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||||
|
await $.http.get(
|
||||||
|
`https://rest.nexmo.com/account/get-balance?api_key=${$.auth.data.apiKey}&api_secret=${$.auth.data.apiSecret}`
|
||||||
|
);
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
screenName: $.auth.data.screenName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
0
packages/backend/src/apps/vonage/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/vonage/index.d.ts
vendored
Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user