Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c762f0562f | ||
![]() |
98274c3d71 |
@@ -5,11 +5,8 @@ BACKEND_PORT=3000
|
|||||||
WEB_PORT=3001
|
WEB_PORT=3001
|
||||||
|
|
||||||
echo "Configuring backend environment variables..."
|
echo "Configuring backend environment variables..."
|
||||||
|
|
||||||
cd packages/backend
|
cd packages/backend
|
||||||
|
|
||||||
rm -rf .env
|
rm -rf .env
|
||||||
|
|
||||||
echo "
|
echo "
|
||||||
PORT=$BACKEND_PORT
|
PORT=$BACKEND_PORT
|
||||||
WEB_APP_URL=http://localhost:$WEB_PORT
|
WEB_APP_URL=http://localhost:$WEB_PORT
|
||||||
@@ -24,35 +21,24 @@ WEBHOOK_SECRET_KEY=sample_webhook_secret_key
|
|||||||
APP_SECRET_KEY=sample_app_secret_key
|
APP_SECRET_KEY=sample_app_secret_key
|
||||||
REDIS_HOST=redis
|
REDIS_HOST=redis
|
||||||
SERVE_WEB_APP_SEPARATELY=true" >> .env
|
SERVE_WEB_APP_SEPARATELY=true" >> .env
|
||||||
|
|
||||||
echo "Installing backend dependencies..."
|
|
||||||
|
|
||||||
yarn
|
|
||||||
|
|
||||||
cd $CURRENT_DIR
|
cd $CURRENT_DIR
|
||||||
|
|
||||||
echo "Configuring web environment variables..."
|
echo "Configuring web environment variables..."
|
||||||
|
|
||||||
cd packages/web
|
cd packages/web
|
||||||
|
|
||||||
rm -rf .env
|
rm -rf .env
|
||||||
|
|
||||||
echo "
|
echo "
|
||||||
PORT=$WEB_PORT
|
PORT=$WEB_PORT
|
||||||
REACT_APP_BACKEND_URL=http://localhost:$BACKEND_PORT
|
REACT_APP_GRAPHQL_URL=http://localhost:$BACKEND_PORT/graphql
|
||||||
" >> .env
|
" >> .env
|
||||||
|
|
||||||
echo "Installing web dependencies..."
|
|
||||||
|
|
||||||
yarn
|
|
||||||
|
|
||||||
cd $CURRENT_DIR
|
cd $CURRENT_DIR
|
||||||
|
|
||||||
|
echo "Installing and linking dependencies..."
|
||||||
|
yarn
|
||||||
|
yarn lerna bootstrap
|
||||||
|
|
||||||
echo "Migrating database..."
|
echo "Migrating database..."
|
||||||
|
|
||||||
cd packages/backend
|
cd packages/backend
|
||||||
|
|
||||||
yarn db:migrate
|
yarn db:migrate
|
||||||
yarn db:seed:user
|
yarn db:seed:user
|
||||||
|
|
||||||
echo "Done!"
|
echo "Done!"
|
@@ -8,7 +8,7 @@
|
|||||||
"version": "latest"
|
"version": "latest"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": 18
|
"version": 20
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/common-utils:1": {
|
"ghcr.io/devcontainers/features/common-utils:1": {
|
||||||
"username": "vscode",
|
"username": "vscode",
|
||||||
|
@@ -36,6 +36,7 @@ services:
|
|||||||
keycloak:
|
keycloak:
|
||||||
image: quay.io/keycloak/keycloak:21.1
|
image: quay.io/keycloak/keycloak:21.1
|
||||||
restart: always
|
restart: always
|
||||||
|
container_name: keycloak
|
||||||
environment:
|
environment:
|
||||||
- KEYCLOAK_ADMIN=admin
|
- KEYCLOAK_ADMIN=admin
|
||||||
- KEYCLOAK_ADMIN_PASSWORD=admin
|
- KEYCLOAK_ADMIN_PASSWORD=admin
|
||||||
|
@@ -4,9 +4,5 @@
|
|||||||
**/.devcontainer
|
**/.devcontainer
|
||||||
**/.github
|
**/.github
|
||||||
**/.vscode
|
**/.vscode
|
||||||
**/.env
|
|
||||||
**/.env.test
|
|
||||||
**/.env.production
|
|
||||||
**/yarn-error.log
|
|
||||||
packages/docs
|
packages/docs
|
||||||
packages/e2e-test
|
packages/e2e-test
|
||||||
|
18
.eslintrc.js
Normal file
18
.eslintrc.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['@typescript-eslint'],
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'prettier',
|
||||||
|
],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['**/*.test.ts', '**/test/**/*.ts'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/ban-ts-comment': ['off'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
@@ -1,52 +0,0 @@
|
|||||||
name: release-tag
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
jobs:
|
|
||||||
release-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
DOCKER_ORG: groot
|
|
||||||
DOCKER_LATEST: latest
|
|
||||||
RUNNER_TOOL_CACHE: /toolcache
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
|
|
||||||
- name: Set up Docker BuildX
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
with: # replace it with your local IP
|
|
||||||
config-inline: |
|
|
||||||
[registry."git.send.nrw"]
|
|
||||||
http = true
|
|
||||||
insecure = true
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: git.send.nrw # replace it with your local IP
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Get Meta
|
|
||||||
id: meta
|
|
||||||
run: |
|
|
||||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
|
||||||
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: ./docker
|
|
||||||
file: ./docker/Dockerfile.compose
|
|
||||||
entrypoint: ./docker/compose-entrypoint.sh
|
|
||||||
platforms: |
|
|
||||||
linux/amd64
|
|
||||||
push: true
|
|
||||||
tags: | # replace it with your local IP and tags
|
|
||||||
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
|
|
||||||
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
|
|
9
.github/workflows/backend.yml
vendored
9
.github/workflows/backend.yml
vendored
@@ -41,11 +41,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn
|
run: cd packages/backend && yarn
|
||||||
working-directory: packages/backend
|
|
||||||
- name: Copy .env-example.test file to .env.test
|
- name: Copy .env-example.test file to .env.test
|
||||||
run: cp .env-example.test .env.test
|
run: cd packages/backend && cp .env-example.test .env.test
|
||||||
working-directory: packages/backend
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: yarn test:coverage
|
run: cd packages/backend && yarn test
|
||||||
working-directory: packages/backend
|
|
||||||
|
28
.github/workflows/ci.yml
vendored
28
.github/workflows/ci.yml
vendored
@@ -18,13 +18,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '18'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: packages/backend/yarn.lock
|
cache-dependency-path: yarn.lock
|
||||||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
||||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn --frozen-lockfile
|
||||||
working-directory: packages/backend
|
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
working-directory: packages/backend
|
|
||||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||||
start-backend-server:
|
start-backend-server:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -37,13 +35,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '18'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: packages/backend/yarn.lock
|
cache-dependency-path: yarn.lock
|
||||||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
||||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn --frozen-lockfile && yarn lerna bootstrap
|
||||||
working-directory: packages/backend
|
- run: cd packages/backend && yarn start
|
||||||
- run: yarn start
|
|
||||||
working-directory: packages/backend
|
|
||||||
env:
|
env:
|
||||||
ENCRYPTION_KEY: sample_encryption_key
|
ENCRYPTION_KEY: sample_encryption_key
|
||||||
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
||||||
@@ -59,13 +55,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '18'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: packages/backend/yarn.lock
|
cache-dependency-path: yarn.lock
|
||||||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
||||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn --frozen-lockfile && yarn lerna bootstrap
|
||||||
working-directory: packages/backend
|
- run: cd packages/backend && yarn start:worker
|
||||||
- run: yarn start:worker
|
|
||||||
working-directory: packages/backend
|
|
||||||
env:
|
env:
|
||||||
ENCRYPTION_KEY: sample_encryption_key
|
ENCRYPTION_KEY: sample_encryption_key
|
||||||
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
||||||
@@ -81,13 +75,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '18'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: packages/web/yarn.lock
|
cache-dependency-path: yarn.lock
|
||||||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
||||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn --frozen-lockfile && yarn lerna bootstrap
|
||||||
working-directory: packages/web
|
- run: cd packages/web && yarn build
|
||||||
- run: yarn build
|
|
||||||
working-directory: packages/web
|
|
||||||
env:
|
env:
|
||||||
CI: false
|
CI: false
|
||||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||||
|
32
.github/workflows/docs-change.yml
vendored
32
.github/workflows/docs-change.yml
vendored
@@ -1,32 +0,0 @@
|
|||||||
name: Automatisch Docs Change
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'packages/docs/**'
|
|
||||||
jobs:
|
|
||||||
label:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Label PR
|
|
||||||
uses: actions/github-script@v6
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const { pull_request } = context.payload;
|
|
||||||
|
|
||||||
const label = 'documentation-change';
|
|
||||||
const hasLabel = pull_request.labels.some(({ name }) => name === label);
|
|
||||||
|
|
||||||
if (!hasLabel) {
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: pull_request.number,
|
|
||||||
labels: [label],
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Label "${label}" added to PR #${pull_request.number}`);
|
|
||||||
} else {
|
|
||||||
console.log(`Label "${label}" already exists on PR #${pull_request.number}`);
|
|
||||||
}
|
|
33
.github/workflows/playwright.yml
vendored
33
.github/workflows/playwright.yml
vendored
@@ -3,13 +3,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
# TODO: Add pull request after optimizing the total excecution time of the test suite.
|
pull_request:
|
||||||
# pull_request:
|
paths:
|
||||||
# paths:
|
- 'packages/backend/**'
|
||||||
# - 'packages/backend/**'
|
- 'packages/e2e-tests/**'
|
||||||
# - 'packages/e2e-tests/**'
|
- 'packages/web/**'
|
||||||
# - 'packages/web/**'
|
- '!packages/backend/src/apps/**'
|
||||||
# - '!packages/backend/src/apps/**'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -59,27 +58,21 @@ jobs:
|
|||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
- name: Install web dependencies
|
- name: Install dependencies
|
||||||
run: yarn
|
run: yarn && yarn lerna bootstrap
|
||||||
working-directory: ./packages/web
|
|
||||||
- name: Install backend dependencies
|
|
||||||
run: yarn
|
|
||||||
working-directory: ./packages/backend
|
|
||||||
- name: Install e2e-tests dependencies
|
|
||||||
run: yarn
|
|
||||||
working-directory: ./packages/e2e-tests
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: yarn playwright install --with-deps
|
run: yarn playwright install --with-deps
|
||||||
working-directory: ./packages/e2e-tests
|
- name: Build Automatisch
|
||||||
- name: Build Automatisch web
|
run: yarn lerna run --scope=@*/{web,cli} build
|
||||||
run: yarn build
|
|
||||||
working-directory: ./packages/web
|
|
||||||
env:
|
env:
|
||||||
# Keep this until we clean up warnings in build processes
|
# Keep this until we clean up warnings in build processes
|
||||||
CI: false
|
CI: false
|
||||||
- name: Migrate database
|
- name: Migrate database
|
||||||
working-directory: ./packages/backend
|
working-directory: ./packages/backend
|
||||||
run: yarn db:migrate
|
run: yarn db:migrate
|
||||||
|
- name: Seed user
|
||||||
|
working-directory: ./packages/backend
|
||||||
|
run: yarn db:seed:user &
|
||||||
- name: Install certutils
|
- name: Install certutils
|
||||||
run: sudo apt install -y libnss3-tools
|
run: sudo apt install -y libnss3-tools
|
||||||
- name: Install mkcert
|
- name: Install mkcert
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ logs
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
version: '3.9'
|
version: '3.9'
|
||||||
services:
|
services:
|
||||||
main:
|
main:
|
||||||
image: git.send.nrw/groot/automatisch:latest
|
build:
|
||||||
|
context: ./docker
|
||||||
|
dockerfile: Dockerfile.compose
|
||||||
|
entrypoint: /compose-entrypoint.sh
|
||||||
ports:
|
ports:
|
||||||
- '3000:3000'
|
- '3000:3000'
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -25,7 +28,10 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- automatisch_storage:/automatisch/storage
|
- automatisch_storage:/automatisch/storage
|
||||||
worker:
|
worker:
|
||||||
image: git.send.nrw/groot/automatisch:latest
|
build:
|
||||||
|
context: ./docker
|
||||||
|
dockerfile: Dockerfile.compose
|
||||||
|
entrypoint: /compose-entrypoint.sh
|
||||||
depends_on:
|
depends_on:
|
||||||
- main
|
- main
|
||||||
environment:
|
environment:
|
||||||
|
@@ -1,27 +1,14 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM node:18-alpine
|
FROM node:18-alpine
|
||||||
|
|
||||||
ENV PORT=3000
|
|
||||||
|
|
||||||
RUN \
|
|
||||||
apk --no-cache add --virtual build-dependencies python3 build-base git make g++
|
|
||||||
|
|
||||||
WORKDIR /automatisch
|
WORKDIR /automatisch
|
||||||
|
|
||||||
# copy the app, note .dockerignore
|
|
||||||
COPY . /automatisch
|
|
||||||
|
|
||||||
RUN cd packages/web && yarn
|
|
||||||
|
|
||||||
RUN cd packages/web && yarn build
|
|
||||||
|
|
||||||
RUN cd packages/backend && yarn --production
|
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
|
apk --no-cache add --virtual build-dependencies python3 build-base && \
|
||||||
|
yarn global add @automatisch/cli@0.10.0 --network-timeout 1000000 && \
|
||||||
rm -rf /usr/local/share/.cache/ && \
|
rm -rf /usr/local/share/.cache/ && \
|
||||||
apk del build-dependencies
|
apk del build-dependencies
|
||||||
|
|
||||||
COPY ./docker/entrypoint.sh /entrypoint.sh
|
COPY ./entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENTRYPOINT ["sh", "/entrypoint.sh"]
|
ENTRYPOINT ["sh", "/entrypoint.sh"]
|
||||||
|
24
docker/Dockerfile.cloud
Normal file
24
docker/Dockerfile.cloud
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
ENV PORT 3000
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apk --no-cache add --virtual build-dependencies python3 build-base git
|
||||||
|
|
||||||
|
RUN git clone https://github.com/automatisch/automatisch.git
|
||||||
|
|
||||||
|
WORKDIR /automatisch
|
||||||
|
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
RUN if [ "$WORKER" != "true" ]; then cd packages/web && yarn build; fi
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
rm -rf /usr/local/share/.cache/ && \
|
||||||
|
apk del build-dependencies
|
||||||
|
|
||||||
|
COPY ./docker/entrypoint-cloud.sh /entrypoint-cloud.sh
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
ENTRYPOINT ["sh", "/entrypoint-cloud.sh"]
|
@@ -1,5 +1,5 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM automatischio/automatisch:latest
|
FROM automatischio/automatisch:0.10.0
|
||||||
WORKDIR /automatisch
|
WORKDIR /automatisch
|
||||||
|
|
||||||
RUN apk add --no-cache openssl dos2unix
|
RUN apk add --no-cache openssl dos2unix
|
||||||
|
13
docker/entrypoint-cloud.sh
Executable file
13
docker/entrypoint-cloud.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd packages/backend
|
||||||
|
|
||||||
|
if [ -n "$WORKER" ]; then
|
||||||
|
yarn start:worker
|
||||||
|
else
|
||||||
|
yarn db:migrate
|
||||||
|
yarn db:seed:user
|
||||||
|
yarn start
|
||||||
|
fi
|
@@ -2,12 +2,8 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
cd packages/backend
|
|
||||||
|
|
||||||
if [ -n "$WORKER" ]; then
|
if [ -n "$WORKER" ]; then
|
||||||
yarn start:worker
|
automatisch start-worker
|
||||||
else
|
else
|
||||||
yarn db:migrate
|
automatisch start
|
||||||
yarn db:seed:user
|
|
||||||
yarn start
|
|
||||||
fi
|
fi
|
||||||
|
13
lerna.json
Normal file
13
lerna.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
|
"version": "0.10.0",
|
||||||
|
"npmClient": "yarn",
|
||||||
|
"useWorkspaces": true,
|
||||||
|
"command": {
|
||||||
|
"add": {
|
||||||
|
"exact": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
package.json
Normal file
35
package.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "@automatisch/root",
|
||||||
|
"license": "See LICENSE file",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "lerna run --stream --parallel --scope=@*/{web,backend} dev",
|
||||||
|
"start:web": "lerna run --stream --scope=@*/web dev",
|
||||||
|
"start:backend": "lerna run --stream --scope=@*/backend dev",
|
||||||
|
"lint": "lerna run --no-bail --stream --parallel --scope=@*/{web,backend} lint",
|
||||||
|
"build:docs": "cd ./packages/docs && yarn install && yarn build"
|
||||||
|
},
|
||||||
|
"workspaces": {
|
||||||
|
"packages": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
|
"nohoist": [
|
||||||
|
"**/babel-loader",
|
||||||
|
"**/webpack",
|
||||||
|
"**/@automatisch/web",
|
||||||
|
"**/ajv"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||||
|
"@typescript-eslint/parser": "^5.9.1",
|
||||||
|
"eslint": "^8.13.0",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"lerna": "^4.0.0",
|
||||||
|
"prettier": "^2.5.1"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,6 @@ import appConfig from '../../src/config/app.js';
|
|||||||
import logger from '../../src/helpers/logger.js';
|
import logger from '../../src/helpers/logger.js';
|
||||||
import client from './client.js';
|
import client from './client.js';
|
||||||
import User from '../../src/models/user.js';
|
import User from '../../src/models/user.js';
|
||||||
import Config from '../../src/models/config.js';
|
|
||||||
import Role from '../../src/models/role.js';
|
import Role from '../../src/models/role.js';
|
||||||
import '../../src/config/orm.js';
|
import '../../src/config/orm.js';
|
||||||
import process from 'process';
|
import process from 'process';
|
||||||
@@ -10,7 +9,7 @@ import process from 'process';
|
|||||||
async function fetchAdminRole() {
|
async function fetchAdminRole() {
|
||||||
const role = await Role.query()
|
const role = await Role.query()
|
||||||
.where({
|
.where({
|
||||||
name: 'Admin',
|
key: 'admin',
|
||||||
})
|
})
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.first();
|
.first();
|
||||||
@@ -22,14 +21,6 @@ export async function createUser(
|
|||||||
email = 'user@automatisch.io',
|
email = 'user@automatisch.io',
|
||||||
password = 'sample'
|
password = 'sample'
|
||||||
) {
|
) {
|
||||||
if (appConfig.disableSeedUser) {
|
|
||||||
logger.info('Seed user is disabled.');
|
|
||||||
|
|
||||||
process.exit(0);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UNIQUE_VIOLATION_CODE = '23505';
|
const UNIQUE_VIOLATION_CODE = '23505';
|
||||||
|
|
||||||
const role = await fetchAdminRole();
|
const role = await fetchAdminRole();
|
||||||
@@ -46,8 +37,6 @@ export async function createUser(
|
|||||||
if (userCount === 0) {
|
if (userCount === 0) {
|
||||||
const user = await User.query().insertAndFetch(userParams);
|
const user = await User.query().insertAndFetch(userParams);
|
||||||
logger.info(`User has been saved: ${user.email}`);
|
logger.info(`User has been saved: ${user.email}`);
|
||||||
|
|
||||||
await Config.markInstallationCompleted();
|
|
||||||
} else {
|
} else {
|
||||||
logger.info('No need to seed a user.');
|
logger.info('No need to seed a user.');
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { knexSnakeCaseMappers } from 'objection';
|
import { knexSnakeCaseMappers } from 'objection';
|
||||||
import appConfig from './src/config/app.js';
|
import appConfig from './src/config/app.js';
|
||||||
import path, { join } from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
const fileExtension = 'js';
|
const fileExtension = 'js';
|
||||||
@@ -20,12 +20,12 @@ const knexConfig = {
|
|||||||
searchPath: [appConfig.postgresSchema],
|
searchPath: [appConfig.postgresSchema],
|
||||||
pool: { min: 0, max: 20 },
|
pool: { min: 0, max: 20 },
|
||||||
migrations: {
|
migrations: {
|
||||||
directory: join(__dirname, '/src/db/migrations'),
|
directory: __dirname + '/src/db/migrations',
|
||||||
extension: fileExtension,
|
extension: fileExtension,
|
||||||
loadExtensions: [`.${fileExtension}`],
|
loadExtensions: [`.${fileExtension}`],
|
||||||
},
|
},
|
||||||
seeds: {
|
seeds: {
|
||||||
directory: join(__dirname, '/src/db/seeds'),
|
directory: __dirname + '/src/db/seeds',
|
||||||
},
|
},
|
||||||
...(appConfig.isTest ? knexSnakeCaseMappers() : {}),
|
...(appConfig.isTest ? knexSnakeCaseMappers() : {}),
|
||||||
};
|
};
|
||||||
|
@@ -5,15 +5,13 @@
|
|||||||
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nodemon --exec node src/server.js",
|
"dev": "nodemon --watch 'src/**/*.js' --exec 'node' src/server.js",
|
||||||
"worker": "nodemon --exec node src/worker.js",
|
"worker": "nodemon --watch 'src/**/*.js' --exec 'node' src/worker.js",
|
||||||
"start": "node src/server.js",
|
"start": "node src/server.js",
|
||||||
"start:worker": "node src/worker.js",
|
"start:worker": "node src/worker.js",
|
||||||
"pretest": "APP_ENV=test node ./test/setup/prepare-test-env.js",
|
"pretest": "APP_ENV=test node ./test/setup/prepare-test-env.js",
|
||||||
"test": "APP_ENV=test vitest run",
|
"test": "APP_ENV=test vitest run",
|
||||||
"test:watch": "APP_ENV=test vitest watch",
|
"lint": "eslint . --ignore-path ../../.eslintignore",
|
||||||
"test:coverage": "yarn test --coverage",
|
|
||||||
"lint": "eslint .",
|
|
||||||
"db:create": "node ./bin/database/create.js",
|
"db:create": "node ./bin/database/create.js",
|
||||||
"db:seed:user": "node ./bin/database/seed-user.js",
|
"db:seed:user": "node ./bin/database/seed-user.js",
|
||||||
"db:drop": "node ./bin/database/drop.js",
|
"db:drop": "node ./bin/database/drop.js",
|
||||||
@@ -24,7 +22,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bull-board/express": "^3.10.1",
|
"@bull-board/express": "^3.10.1",
|
||||||
"@casl/ability": "^6.5.0",
|
"@casl/ability": "^6.5.0",
|
||||||
"@faker-js/faker": "^9.2.0",
|
"@graphql-tools/graphql-file-loader": "^7.3.4",
|
||||||
|
"@graphql-tools/load": "^7.5.2",
|
||||||
"@node-saml/passport-saml": "^4.0.4",
|
"@node-saml/passport-saml": "^4.0.4",
|
||||||
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
||||||
"@sentry/node": "^7.42.0",
|
"@sentry/node": "^7.42.0",
|
||||||
@@ -32,24 +31,25 @@
|
|||||||
"accounting": "^0.4.1",
|
"accounting": "^0.4.1",
|
||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"axios": "1.6.0",
|
"axios": "1.6.0",
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.0.1",
|
||||||
"bullmq": "^3.0.0",
|
"bullmq": "^3.0.0",
|
||||||
|
"copyfiles": "^2.4.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"debug": "~2.6.9",
|
"debug": "~2.6.9",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"eslint": "^8.13.0",
|
|
||||||
"eslint-config-prettier": "^8.3.0",
|
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
|
||||||
"express": "~4.18.2",
|
"express": "~4.18.2",
|
||||||
"express-async-errors": "^3.1.1",
|
|
||||||
"express-basic-auth": "^1.2.1",
|
"express-basic-auth": "^1.2.1",
|
||||||
|
"express-graphql": "^0.12.0",
|
||||||
"fast-xml-parser": "^4.0.11",
|
"fast-xml-parser": "^4.0.11",
|
||||||
|
"graphql-middleware": "^6.1.15",
|
||||||
|
"graphql-shield": "^7.5.0",
|
||||||
|
"graphql-tools": "^8.2.0",
|
||||||
|
"graphql-type-json": "^0.3.2",
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"http-errors": "~1.6.3",
|
"http-errors": "~1.6.3",
|
||||||
"http-proxy-agent": "^7.0.0",
|
"http-proxy-agent": "^7.0.0",
|
||||||
"https-proxy-agent": "^7.0.1",
|
"https-proxy-agent": "^7.0.1",
|
||||||
"isolated-vm": "^5.0.1",
|
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"knex": "^2.4.0",
|
"knex": "^2.4.0",
|
||||||
"libphonenumber-js": "^1.10.48",
|
"libphonenumber-js": "^1.10.48",
|
||||||
@@ -66,10 +66,9 @@
|
|||||||
"pg": "^8.7.1",
|
"pg": "^8.7.1",
|
||||||
"php-serialize": "^4.0.2",
|
"php-serialize": "^4.0.2",
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"prettier": "^2.5.1",
|
|
||||||
"raw-body": "^2.5.2",
|
"raw-body": "^2.5.2",
|
||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"uuid": "^9.0.1",
|
"stripe": "^11.13.0",
|
||||||
"winston": "^3.7.1",
|
"winston": "^3.7.1",
|
||||||
"xmlrpc": "^1.3.2"
|
"xmlrpc": "^1.3.2"
|
||||||
},
|
},
|
||||||
@@ -98,19 +97,11 @@
|
|||||||
"url": "https://github.com/automatisch/automatisch/issues"
|
"url": "https://github.com/automatisch/automatisch/issues"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitest/coverage-v8": "^2.1.5",
|
|
||||||
"node-gyp": "^10.1.0",
|
|
||||||
"nodemon": "^2.0.13",
|
"nodemon": "^2.0.13",
|
||||||
"supertest": "^6.3.3",
|
"supertest": "^6.3.3",
|
||||||
"vitest": "^2.1.5"
|
"vitest": "^1.1.3"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
|
||||||
"nodemonConfig": {
|
|
||||||
"watch": [
|
|
||||||
"src/"
|
|
||||||
],
|
|
||||||
"ext": "js"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import createError from 'http-errors';
|
import createError from 'http-errors';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import 'express-async-errors';
|
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
|
|
||||||
import appConfig from './config/app.js';
|
import appConfig from './config/app.js';
|
||||||
|
1
packages/backend/src/apps/airbrake/assets/favicon.svg
Normal file
1
packages/backend/src/apps/airbrake/assets/favicon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg height="255" preserveAspectRatio="xMidYMid" viewBox="0 0 256 255" width="256" xmlns="http://www.w3.org/2000/svg"><path d="m128.636514 155.746615v-155.23361889h-3.522242v.06873152l-124.60824865 64.03287157v60.8642488h.00597665v3.234366h-.00597665v60.868233l124.60824865 64.747082h3.842989v-98.581914z" fill="#ff8e4a"/><path d="m129.941416 254.328529 125.568498-64.747082v-124.9668478l-125.887253-64.10160309h-2.243237v253.81055289h2.243237" fill="#f48746"/><path d="m109.097837 87.2551595h36.19561v59.2077195h-36.19561z" fill="#ff8e4a"/><path d="m66.1735097 188.397074h14.8639378c9.4102412 0 12.6087471-2.238257 15.6189883-9.988981l8.2796572-21.353587h45.159596l8.280653 21.353587c3.011238 7.750724 6.396016 9.988981 15.805261 9.988981h14.677665v-19.114335h-3.011237c-3.19751 0-4.704622-.689307-5.831222-3.790194l-39.516638-99.3658524h-25.779299l-39.703907 99.3658524c-1.1285915 3.100887-2.632716 3.790194-5.833214 3.790194h-3.0102413zm44.4075333-49.939922 11.478163-30.655253c2.445448-6.714771 5.269417-18.2556889 5.269417-18.2556889h.375533s2.822972 11.5409179 5.269416 18.2556889l11.478163 30.655253z" fill="#fff"/><path d="m231.204856 150.082739v-51.8086223c.235082 4.5233303 2.970397 16.8432063 24.305058 27.8512063v11.653479zm0-53.1623343v1.353712c-.029883-.5926848-.01793-1.0479066 0-1.353712zm.041837-.4392841s-.022911.1534008-.041837.4392841v-.4392841z" fill="#d4763c"/><path d="m231.155051 94.3016342c-.013946.9931207.05877 1.8945993.049805 2.0460078-.01793.2480312-2.220327 16.094132 24.305058 29.777681v-60.863253c-23.325883 12.0349884-24.449494 25.7414475-24.354863 29.0395642" fill="#ff8e4a"/></svg>
|
After Width: | Height: | Size: 1.6 KiB |
@@ -16,14 +16,25 @@ export default {
|
|||||||
clickToCopy: false,
|
clickToCopy: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'apiKey',
|
key: 'instanceUrl',
|
||||||
label: 'API Key',
|
label: 'Instance URL',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
value: null,
|
value: null,
|
||||||
placeholder: null,
|
placeholder: null,
|
||||||
description: 'MailerLite API key of your account.',
|
description: 'Your subdomain as https://{yoursubdomain}.airbrake.io',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'authToken',
|
||||||
|
label: 'Auth Token',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: 'Airbrake Auth Token of your account.',
|
||||||
clickToCopy: false,
|
clickToCopy: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
@@ -0,0 +1,14 @@
|
|||||||
|
const verifyCredentials = async ($) => {
|
||||||
|
await $.http.get(`/api/v4/projects?key=${$.auth.data.authToken}`, {
|
||||||
|
additionalProperties: {
|
||||||
|
skipAddingAuthToken: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
screenName: $.auth.data.screenName,
|
||||||
|
authToken: $.auth.data.authToken,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
10
packages/backend/src/apps/airbrake/common/add-auth-token.js
Normal file
10
packages/backend/src/apps/airbrake/common/add-auth-token.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const addAuthToken = ($, requestConfig) => {
|
||||||
|
if (requestConfig.additionalProperties?.skipAddingAuthToken)
|
||||||
|
return requestConfig;
|
||||||
|
|
||||||
|
requestConfig.url = requestConfig.url + `?key=${$.auth.data.authToken}`;
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addAuthToken;
|
11
packages/backend/src/apps/airbrake/common/set-base-url.js
Normal file
11
packages/backend/src/apps/airbrake/common/set-base-url.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const setBaseUrl = ($, requestConfig) => {
|
||||||
|
const subdomain = $.auth.data.instanceUrl;
|
||||||
|
|
||||||
|
if (subdomain) {
|
||||||
|
requestConfig.baseURL = `https://${subdomain}.airbrake.io`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default setBaseUrl;
|
3
packages/backend/src/apps/airbrake/dynamic-data/index.js
Normal file
3
packages/backend/src/apps/airbrake/dynamic-data/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import listProjects from './list-projects/index.js';
|
||||||
|
|
||||||
|
export default [listProjects];
|
@@ -0,0 +1,23 @@
|
|||||||
|
export default {
|
||||||
|
name: 'List projects',
|
||||||
|
key: 'listProjects',
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const projects = {
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await $.http.get('/api/v4/projects');
|
||||||
|
|
||||||
|
if (data.projects.length) {
|
||||||
|
for (const project of data.projects) {
|
||||||
|
projects.data.push({
|
||||||
|
value: project.id,
|
||||||
|
name: project.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return projects;
|
||||||
|
},
|
||||||
|
};
|
21
packages/backend/src/apps/airbrake/index.js
Normal file
21
packages/backend/src/apps/airbrake/index.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import defineApp from '../../helpers/define-app.js';
|
||||||
|
import setBaseUrl from './common/set-base-url.js';
|
||||||
|
import auth from './auth/index.js';
|
||||||
|
import addAuthToken from './common/add-auth-token.js';
|
||||||
|
import triggers from './triggers/index.js';
|
||||||
|
import dynamicData from './dynamic-data/index.js';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'Airbrake',
|
||||||
|
key: 'airbrake',
|
||||||
|
iconUrl: '{BASE_URL}/apps/airbrake/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/airbrake/connection',
|
||||||
|
supportsConnections: true,
|
||||||
|
baseUrl: 'https://www.airbrake.io',
|
||||||
|
apiBaseUrl: '',
|
||||||
|
primaryColor: 'f58c54',
|
||||||
|
beforeRequest: [setBaseUrl, addAuthToken],
|
||||||
|
auth,
|
||||||
|
triggers,
|
||||||
|
dynamicData,
|
||||||
|
});
|
3
packages/backend/src/apps/airbrake/triggers/index.js
Normal file
3
packages/backend/src/apps/airbrake/triggers/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import newErrors from './new-errors/index.js';
|
||||||
|
|
||||||
|
export default [newErrors];
|
@@ -0,0 +1,66 @@
|
|||||||
|
//import { URLSearchParams } from 'node:url';
|
||||||
|
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||||
|
|
||||||
|
export default defineTrigger({
|
||||||
|
name: 'New errors',
|
||||||
|
key: 'newErrors',
|
||||||
|
pollInterval: 15,
|
||||||
|
description: 'Triggers when a new error occurs.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Project',
|
||||||
|
key: 'projectId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listProjects',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const projectId = $.step.parameters.projectId;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
limit: 100,
|
||||||
|
page: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let next = false;
|
||||||
|
do {
|
||||||
|
const { data } = await $.http.get(
|
||||||
|
`/api/v4/projects/${projectId}/groups`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data.count > params.limit) {
|
||||||
|
params.page = params.page + 1;
|
||||||
|
next = true;
|
||||||
|
} else {
|
||||||
|
next = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data?.groups?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const group of data.groups) {
|
||||||
|
$.pushTriggerItem({
|
||||||
|
raw: group,
|
||||||
|
meta: {
|
||||||
|
internalId: group.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} while (next);
|
||||||
|
},
|
||||||
|
});
|
@@ -1,92 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Create record',
|
|
||||||
key: 'createRecord',
|
|
||||||
description: 'Creates a new record with fields that automatically populate.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Base',
|
|
||||||
key: 'baseId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: 'Base in which to create the record.',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listBases',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Table',
|
|
||||||
key: 'tableId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.baseId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listTables',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.baseId',
|
|
||||||
value: '{parameters.baseId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
additionalFields: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicFields',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listFields',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.baseId',
|
|
||||||
value: '{parameters.baseId}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.tableId',
|
|
||||||
value: '{parameters.tableId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const { baseId, tableId, ...rest } = $.step.parameters;
|
|
||||||
|
|
||||||
const fields = Object.entries(rest).reduce((result, [key, value]) => {
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
result[key] = value.map((item) => item.value);
|
|
||||||
} else if (value !== '') {
|
|
||||||
result[key] = value;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
typecast: true,
|
|
||||||
fields,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.post(`/v0/${baseId}/${tableId}`, body);
|
|
||||||
|
|
||||||
$.setActionItem({
|
|
||||||
raw: data,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,174 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
import { URLSearchParams } from 'url';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Find record',
|
|
||||||
key: 'findRecord',
|
|
||||||
description:
|
|
||||||
"Finds a record using simple field search or use Airtable's formula syntax to find a matching record.",
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Base',
|
|
||||||
key: 'baseId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: 'Base in which to create the record.',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listBases',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Table',
|
|
||||||
key: 'tableId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.baseId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listTables',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.baseId',
|
|
||||||
value: '{parameters.baseId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Search by field',
|
|
||||||
key: 'tableField',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.baseId', 'parameters.tableId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listTableFields',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.baseId',
|
|
||||||
value: '{parameters.baseId}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.tableId',
|
|
||||||
value: '{parameters.tableId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Search Value',
|
|
||||||
key: 'searchValue',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
description:
|
|
||||||
'The value of unique identifier for the record. For date values, please use the ISO format (e.g., "YYYY-MM-DD").',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Search for exact match?',
|
|
||||||
key: 'exactMatch',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
options: [
|
|
||||||
{ label: 'Yes', value: 'true' },
|
|
||||||
{ label: 'No', value: 'false' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Search Formula',
|
|
||||||
key: 'searchFormula',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
description:
|
|
||||||
'Instead, you have the option to use an Airtable search formula for locating records according to sophisticated criteria and across various fields.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Limit to View',
|
|
||||||
key: 'limitToView',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.baseId', 'parameters.tableId'],
|
|
||||||
description:
|
|
||||||
'You have the choice to restrict the search to a particular view ID if desired.',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listTableViews',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.baseId',
|
|
||||||
value: '{parameters.baseId}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.tableId',
|
|
||||||
value: '{parameters.tableId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const {
|
|
||||||
baseId,
|
|
||||||
tableId,
|
|
||||||
tableField,
|
|
||||||
searchValue,
|
|
||||||
exactMatch,
|
|
||||||
searchFormula,
|
|
||||||
limitToView,
|
|
||||||
} = $.step.parameters;
|
|
||||||
|
|
||||||
let filterByFormula;
|
|
||||||
|
|
||||||
if (tableField && searchValue) {
|
|
||||||
filterByFormula =
|
|
||||||
exactMatch === 'true'
|
|
||||||
? `{${tableField}} = '${searchValue}'`
|
|
||||||
: `LOWER({${tableField}}) = LOWER('${searchValue}')`;
|
|
||||||
} else {
|
|
||||||
filterByFormula = searchFormula;
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = new URLSearchParams({
|
|
||||||
filterByFormula,
|
|
||||||
view: limitToView,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data } = await $.http.post(
|
|
||||||
`/v0/${baseId}/${tableId}/listRecords`,
|
|
||||||
body
|
|
||||||
);
|
|
||||||
|
|
||||||
$.setActionItem({
|
|
||||||
raw: data,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,4 +0,0 @@
|
|||||||
import createRecord from './create-record/index.js';
|
|
||||||
import findRecord from './find-record/index.js';
|
|
||||||
|
|
||||||
export default [createRecord, findRecord];
|
|
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg width="256px" height="215px" viewBox="0 0 256 215" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
|
||||||
<g>
|
|
||||||
<path d="M114.25873,2.70101695 L18.8604023,42.1756384 C13.5552723,44.3711638 13.6102328,51.9065311 18.9486282,54.0225085 L114.746142,92.0117514 C123.163769,95.3498757 132.537419,95.3498757 140.9536,92.0117514 L236.75256,54.0225085 C242.08951,51.9065311 242.145916,44.3711638 236.83934,42.1756384 L141.442459,2.70101695 C132.738459,-0.900338983 122.961284,-0.900338983 114.25873,2.70101695" fill="#FFBF00"></path>
|
|
||||||
<path d="M136.349071,112.756863 L136.349071,207.659101 C136.349071,212.173089 140.900664,215.263892 145.096461,213.600615 L251.844122,172.166219 C254.281184,171.200072 255.879376,168.845451 255.879376,166.224705 L255.879376,71.3224678 C255.879376,66.8084791 251.327783,63.7176768 247.131986,65.3809537 L140.384325,106.815349 C137.94871,107.781496 136.349071,110.136118 136.349071,112.756863" fill="#26B5F8"></path>
|
|
||||||
<path d="M111.422771,117.65355 L79.742409,132.949912 L76.5257763,134.504714 L9.65047684,166.548104 C5.4112904,168.593211 0.000578531073,165.503855 0.000578531073,160.794612 L0.000578531073,71.7210757 C0.000578531073,70.0173017 0.874160452,68.5463864 2.04568588,67.4384994 C2.53454463,66.9481944 3.08848814,66.5446689 3.66412655,66.2250305 C5.26231864,65.2661153 7.54173107,65.0101153 9.47981017,65.7766689 L110.890522,105.957098 C116.045234,108.002206 116.450206,115.225166 111.422771,117.65355" fill="#ED3049"></path>
|
|
||||||
<path d="M111.422771,117.65355 L79.742409,132.949912 L2.04568588,67.4384994 C2.53454463,66.9481944 3.08848814,66.5446689 3.66412655,66.2250305 C5.26231864,65.2661153 7.54173107,65.0101153 9.47981017,65.7766689 L110.890522,105.957098 C116.045234,108.002206 116.450206,115.225166 111.422771,117.65355" fill-opacity="0.25" fill="#000000"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,38 +0,0 @@
|
|||||||
import crypto from 'crypto';
|
|
||||||
import { URLSearchParams } from 'url';
|
|
||||||
import authScope from '../common/auth-scope.js';
|
|
||||||
|
|
||||||
export default async function generateAuthUrl($) {
|
|
||||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
|
||||||
(field) => field.key == 'oAuthRedirectUrl'
|
|
||||||
);
|
|
||||||
const redirectUri = oauthRedirectUrlField.value;
|
|
||||||
const state = crypto.randomBytes(100).toString('base64url');
|
|
||||||
const codeVerifier = crypto.randomBytes(96).toString('base64url');
|
|
||||||
const codeChallenge = crypto
|
|
||||||
.createHash('sha256')
|
|
||||||
.update(codeVerifier)
|
|
||||||
.digest('base64')
|
|
||||||
.replace(/=/g, '')
|
|
||||||
.replace(/\+/g, '-')
|
|
||||||
.replace(/\//g, '_');
|
|
||||||
|
|
||||||
const searchParams = new URLSearchParams({
|
|
||||||
client_id: $.auth.data.clientId,
|
|
||||||
redirect_uri: redirectUri,
|
|
||||||
response_type: 'code',
|
|
||||||
scope: authScope.join(' '),
|
|
||||||
state,
|
|
||||||
code_challenge: codeChallenge,
|
|
||||||
code_challenge_method: 'S256',
|
|
||||||
});
|
|
||||||
|
|
||||||
const url = `https://airtable.com/oauth2/v1/authorize?${searchParams.toString()}`;
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
url,
|
|
||||||
originalCodeChallenge: codeChallenge,
|
|
||||||
originalState: state,
|
|
||||||
codeVerifier,
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,48 +0,0 @@
|
|||||||
import generateAuthUrl from './generate-auth-url.js';
|
|
||||||
import verifyCredentials from './verify-credentials.js';
|
|
||||||
import refreshToken from './refresh-token.js';
|
|
||||||
import isStillVerified from './is-still-verified.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
key: 'oAuthRedirectUrl',
|
|
||||||
label: 'OAuth Redirect URL',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: true,
|
|
||||||
value: '{WEB_APP_URL}/app/airtable/connections/add',
|
|
||||||
placeholder: null,
|
|
||||||
description:
|
|
||||||
'When asked to input a redirect URL in Airtable, enter the URL above.',
|
|
||||||
clickToCopy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'clientId',
|
|
||||||
label: 'Client ID',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'clientSecret',
|
|
||||||
label: 'Client Secret',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
generateAuthUrl,
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
refreshToken,
|
|
||||||
};
|
|
@@ -1,8 +0,0 @@
|
|||||||
import getCurrentUser from '../common/get-current-user.js';
|
|
||||||
|
|
||||||
const isStillVerified = async ($) => {
|
|
||||||
const currentUser = await getCurrentUser($);
|
|
||||||
return !!currentUser.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default isStillVerified;
|
|
@@ -1,40 +0,0 @@
|
|||||||
import { URLSearchParams } from 'node:url';
|
|
||||||
|
|
||||||
import authScope from '../common/auth-scope.js';
|
|
||||||
|
|
||||||
const refreshToken = async ($) => {
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
client_id: $.auth.data.clientId,
|
|
||||||
grant_type: 'refresh_token',
|
|
||||||
refresh_token: $.auth.data.refreshToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
const basicAuthToken = Buffer.from(
|
|
||||||
$.auth.data.clientId + ':' + $.auth.data.clientSecret
|
|
||||||
).toString('base64');
|
|
||||||
|
|
||||||
const { data } = await $.http.post(
|
|
||||||
'https://airtable.com/oauth2/v1/token',
|
|
||||||
params.toString(),
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
Authorization: `Basic ${basicAuthToken}`,
|
|
||||||
},
|
|
||||||
additionalProperties: {
|
|
||||||
skipAddingAuthHeader: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
accessToken: data.access_token,
|
|
||||||
refreshToken: data.refresh_token,
|
|
||||||
expiresIn: data.expires_in,
|
|
||||||
refreshExpiresIn: data.refresh_expires_in,
|
|
||||||
scope: authScope.join(' '),
|
|
||||||
tokenType: data.token_type,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default refreshToken;
|
|
@@ -1,56 +0,0 @@
|
|||||||
import getCurrentUser from '../common/get-current-user.js';
|
|
||||||
|
|
||||||
const verifyCredentials = async ($) => {
|
|
||||||
if ($.auth.data.originalState !== $.auth.data.state) {
|
|
||||||
throw new Error("The 'state' parameter does not match.");
|
|
||||||
}
|
|
||||||
if ($.auth.data.originalCodeChallenge !== $.auth.data.code_challenge) {
|
|
||||||
throw new Error("The 'code challenge' parameter does not match.");
|
|
||||||
}
|
|
||||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
|
||||||
(field) => field.key == 'oAuthRedirectUrl'
|
|
||||||
);
|
|
||||||
const redirectUri = oauthRedirectUrlField.value;
|
|
||||||
const basicAuthToken = Buffer.from(
|
|
||||||
$.auth.data.clientId + ':' + $.auth.data.clientSecret
|
|
||||||
).toString('base64');
|
|
||||||
|
|
||||||
const { data } = await $.http.post(
|
|
||||||
'https://airtable.com/oauth2/v1/token',
|
|
||||||
{
|
|
||||||
code: $.auth.data.code,
|
|
||||||
client_id: $.auth.data.clientId,
|
|
||||||
redirect_uri: redirectUri,
|
|
||||||
grant_type: 'authorization_code',
|
|
||||||
code_verifier: $.auth.data.codeVerifier,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
Authorization: `Basic ${basicAuthToken}`,
|
|
||||||
},
|
|
||||||
additionalProperties: {
|
|
||||||
skipAddingAuthHeader: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
accessToken: data.access_token,
|
|
||||||
tokenType: data.token_type,
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentUser = await getCurrentUser($);
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
clientId: $.auth.data.clientId,
|
|
||||||
clientSecret: $.auth.data.clientSecret,
|
|
||||||
scope: $.auth.data.scope,
|
|
||||||
expiresIn: data.expires_in,
|
|
||||||
refreshExpiresIn: data.refresh_expires_in,
|
|
||||||
refreshToken: data.refresh_token,
|
|
||||||
screenName: currentUser.email,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
@@ -1,12 +0,0 @@
|
|||||||
const addAuthHeader = ($, requestConfig) => {
|
|
||||||
if (
|
|
||||||
!requestConfig.additionalProperties?.skipAddingAuthHeader &&
|
|
||||||
$.auth.data?.accessToken
|
|
||||||
) {
|
|
||||||
requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -1,12 +0,0 @@
|
|||||||
const authScope = [
|
|
||||||
'data.records:read',
|
|
||||||
'data.records:write',
|
|
||||||
'data.recordComments:read',
|
|
||||||
'data.recordComments:write',
|
|
||||||
'schema.bases:read',
|
|
||||||
'schema.bases:write',
|
|
||||||
'user.email:read',
|
|
||||||
'webhook:manage',
|
|
||||||
];
|
|
||||||
|
|
||||||
export default authScope;
|
|
@@ -1,6 +0,0 @@
|
|||||||
const getCurrentUser = async ($) => {
|
|
||||||
const { data: currentUser } = await $.http.get('/v0/meta/whoami');
|
|
||||||
return currentUser;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getCurrentUser;
|
|
@@ -1,6 +0,0 @@
|
|||||||
import listBases from './list-bases/index.js';
|
|
||||||
import listTableFields from './list-table-fields/index.js';
|
|
||||||
import listTableViews from './list-table-views/index.js';
|
|
||||||
import listTables from './list-tables/index.js';
|
|
||||||
|
|
||||||
export default [listBases, listTableFields, listTableViews, listTables];
|
|
@@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List bases',
|
|
||||||
key: 'listBases',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const bases = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const params = {};
|
|
||||||
|
|
||||||
do {
|
|
||||||
const { data } = await $.http.get('/v0/meta/bases', { params });
|
|
||||||
params.offset = data.offset;
|
|
||||||
|
|
||||||
if (data?.bases) {
|
|
||||||
for (const base of data.bases) {
|
|
||||||
bases.data.push({
|
|
||||||
value: base.id,
|
|
||||||
name: base.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (params.offset);
|
|
||||||
|
|
||||||
return bases;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,39 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List table fields',
|
|
||||||
key: 'listTableFields',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const tableFields = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const { baseId, tableId } = $.step.parameters;
|
|
||||||
|
|
||||||
if (!baseId) {
|
|
||||||
return tableFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {};
|
|
||||||
|
|
||||||
do {
|
|
||||||
const { data } = await $.http.get(`/v0/meta/bases/${baseId}/tables`, {
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
params.offset = data.offset;
|
|
||||||
|
|
||||||
if (data?.tables) {
|
|
||||||
for (const table of data.tables) {
|
|
||||||
if (table.id === tableId) {
|
|
||||||
table.fields.forEach((field) => {
|
|
||||||
tableFields.data.push({
|
|
||||||
value: field.name,
|
|
||||||
name: field.name,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (params.offset);
|
|
||||||
|
|
||||||
return tableFields;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,39 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List table views',
|
|
||||||
key: 'listTableViews',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const tableViews = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const { baseId, tableId } = $.step.parameters;
|
|
||||||
|
|
||||||
if (!baseId) {
|
|
||||||
return tableViews;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {};
|
|
||||||
|
|
||||||
do {
|
|
||||||
const { data } = await $.http.get(`/v0/meta/bases/${baseId}/tables`, {
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
params.offset = data.offset;
|
|
||||||
|
|
||||||
if (data?.tables) {
|
|
||||||
for (const table of data.tables) {
|
|
||||||
if (table.id === tableId) {
|
|
||||||
table.views.forEach((view) => {
|
|
||||||
tableViews.data.push({
|
|
||||||
value: view.id,
|
|
||||||
name: view.name,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (params.offset);
|
|
||||||
|
|
||||||
return tableViews;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,35 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List tables',
|
|
||||||
key: 'listTables',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const tables = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const baseId = $.step.parameters.baseId;
|
|
||||||
|
|
||||||
if (!baseId) {
|
|
||||||
return tables;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {};
|
|
||||||
|
|
||||||
do {
|
|
||||||
const { data } = await $.http.get(`/v0/meta/bases/${baseId}/tables`, {
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
params.offset = data.offset;
|
|
||||||
|
|
||||||
if (data?.tables) {
|
|
||||||
for (const table of data.tables) {
|
|
||||||
tables.data.push({
|
|
||||||
value: table.id,
|
|
||||||
name: table.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (params.offset);
|
|
||||||
|
|
||||||
return tables;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,3 +0,0 @@
|
|||||||
import listFields from './list-fields/index.js';
|
|
||||||
|
|
||||||
export default [listFields];
|
|
@@ -1,86 +0,0 @@
|
|||||||
const hasValue = (value) => value !== null && value !== undefined;
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'List fields',
|
|
||||||
key: 'listFields',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const options = [];
|
|
||||||
const { baseId, tableId } = $.step.parameters;
|
|
||||||
|
|
||||||
if (!hasValue(baseId) || !hasValue(tableId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.get(`/v0/meta/bases/${baseId}/tables`);
|
|
||||||
|
|
||||||
const selectedTable = data.tables.find((table) => table.id === tableId);
|
|
||||||
|
|
||||||
if (!selectedTable) return;
|
|
||||||
|
|
||||||
selectedTable.fields.forEach((field) => {
|
|
||||||
if (field.type === 'singleSelect') {
|
|
||||||
options.push({
|
|
||||||
label: field.name,
|
|
||||||
key: field.name,
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
options: field.options.choices.map((choice) => ({
|
|
||||||
label: choice.name,
|
|
||||||
value: choice.id,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
} else if (field.type === 'multipleSelects') {
|
|
||||||
options.push({
|
|
||||||
label: field.name,
|
|
||||||
key: field.name,
|
|
||||||
type: 'dynamic',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
label: 'Value',
|
|
||||||
key: 'value',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
options: field.options.choices.map((choice) => ({
|
|
||||||
label: choice.name,
|
|
||||||
value: choice.id,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else if (field.type === 'checkbox') {
|
|
||||||
options.push({
|
|
||||||
label: field.name,
|
|
||||||
key: field.name,
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Yes',
|
|
||||||
value: 'true',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'No',
|
|
||||||
value: 'false',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
options.push({
|
|
||||||
label: field.name,
|
|
||||||
key: field.name,
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,22 +0,0 @@
|
|||||||
import defineApp from '../../helpers/define-app.js';
|
|
||||||
import addAuthHeader from './common/add-auth-header.js';
|
|
||||||
import auth from './auth/index.js';
|
|
||||||
import actions from './actions/index.js';
|
|
||||||
import dynamicData from './dynamic-data/index.js';
|
|
||||||
import dynamicFields from './dynamic-fields/index.js';
|
|
||||||
|
|
||||||
export default defineApp({
|
|
||||||
name: 'Airtable',
|
|
||||||
key: 'airtable',
|
|
||||||
baseUrl: 'https://airtable.com',
|
|
||||||
apiBaseUrl: 'https://api.airtable.com',
|
|
||||||
iconUrl: '{BASE_URL}/apps/airtable/assets/favicon.svg',
|
|
||||||
authDocUrl: '{DOCS_URL}/apps/airtable/connection',
|
|
||||||
primaryColor: '#FFBF00',
|
|
||||||
supportsConnections: true,
|
|
||||||
beforeRequest: [addAuthHeader],
|
|
||||||
auth,
|
|
||||||
actions,
|
|
||||||
dynamicData,
|
|
||||||
dynamicFields,
|
|
||||||
});
|
|
@@ -1 +0,0 @@
|
|||||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="132" height="24" fill="none" viewBox="0 0 132 24"><path fill="#19191C" d="M38.557 19.495c2.16 0 3.25-1.113 3.725-1.87h.214c.094.805.664 1.562 1.779 1.562h2.111V16.82h-.545c-.38 0-.57-.213-.57-.545V6.776h-2.8v1.516h-.213c-.545-.758-1.684-1.824-3.772-1.824-3.321 0-5.789 2.748-5.789 6.514s2.515 6.513 5.86 6.513m.498-2.7c-1.969 0-3.51-1.445-3.51-3.79 0-2.297 1.494-3.86 3.487-3.86 1.898 0 3.487 1.397 3.487 3.86 0 2.108-1.352 3.79-3.463 3.79M48.04 24h2.799v-6.376h.213c.522.758 1.637 1.871 3.844 1.871 3.321 0 5.741-2.795 5.741-6.513 0-3.743-2.586-6.514-5.931-6.514-2.135 0-3.18 1.16-3.678 1.8h-.213V6.776h-2.776V24m6.263-7.134c-1.922 0-3.512-1.42-3.512-3.884 0-2.108 1.353-3.885 3.464-3.885 1.97 0 3.511 1.54 3.511 3.885 0 2.297-1.494 3.884-3.463 3.884M62.082 24h2.8v-6.376h.213c.522.758 1.637 1.871 3.843 1.871 3.321 0 5.51-2.795 5.51-6.513 0-3.743-2.355-6.514-5.7-6.514-2.135 0-3.179 1.16-3.677 1.8h-.214V6.776h-2.775zm6.263-7.134c-1.922 0-3.511-1.42-3.511-3.884 0-2.108 1.352-3.885 3.463-3.885 1.97 0 3.512 1.54 3.512 3.885 0 2.297-1.495 3.884-3.464 3.884m9.805 2.61h3.961l2.254-9.735h.143l2.253 9.735H90.7l3.153-12.412h-2.821l-2.254 9.759h-.214l-2.253-9.759h-3.725l-2.278 9.759h-.213l-2.23-9.759h-2.99l3.274 12.412m17.123 0h2.8V13.34c0-2.345 1.09-3.79 3.131-3.79h1.233V6.756h-.925c-1.59 0-2.8 1.09-3.274 2.132h-.19V7.064h-2.775zm21.057 0h2.183v-2.487h-2.159c-.854 0-1.21-.38-1.21-1.256V9.528h3.511V7.064h-3.511V3.582h-2.657v3.482h-2.325v2.464h2.159v6.229c0 2.63 1.589 3.719 4.009 3.719m9.693.019c2.586 0 4.864-1.279 5.67-3.86l-2.562-.616c-.451 1.373-1.755 2.084-3.131 2.084-2.041 0-3.393-1.326-3.417-3.41h9.419v-.782c0-3.695-2.301-6.443-6.097-6.443-3.346 0-6.216 2.63-6.216 6.537 0 3.79 2.538 6.49 6.334 6.49m-3.416-7.84c.166-1.492 1.518-2.747 3.298-2.747 1.708 0 3.108 1.066 3.25 2.747h-6.548"/><path fill="#19191C" fill-rule="evenodd" d="M108.916 19.476h-2.8V9.528h-2.182V7.064h4.982z" clip-rule="evenodd"/><path fill="#19191C" d="M107.309 5.342c1.02 0 1.779-.758 1.779-1.753 0-.971-.759-1.73-1.779-1.73-1.021 0-1.78.759-1.78 1.73 0 .995.759 1.753 1.78 1.753"/><path fill="#FD366E" d="M24.443 16.432v5.478H10.752c-3.989 0-7.472-2.203-9.335-5.478A11.041 11.041 0 0 1 0 11.695v-1.48a10.97 10.97 0 0 1 .381-2.247C1.661 3.368 5.82 0 10.751 0c4.934 0 9.092 3.37 10.371 7.967h-5.854c-.96-1.499-2.624-2.49-4.516-2.49s-3.555.991-4.516 2.49a5.47 5.47 0 0 0-.67 1.494 5.562 5.562 0 0 0-.202 1.494 5.5 5.5 0 0 0 1.69 3.983 5.32 5.32 0 0 0 3.698 1.494h13.69"/><path fill="#FD366E" d="M24.443 9.46v5.478h-9.994a5.5 5.5 0 0 0 1.691-3.983 5.56 5.56 0 0 0-.203-1.494h8.506"/></svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,65 +0,0 @@
|
|||||||
import verifyCredentials from './verify-credentials.js';
|
|
||||||
import isStillVerified from './is-still-verified.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
key: 'screenName',
|
|
||||||
label: 'Screen Name',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description:
|
|
||||||
'Screen name of your connection to be used on Automatisch UI.',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'projectId',
|
|
||||||
label: 'Project ID',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: 'Project ID of your Appwrite project.',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'apiKey',
|
|
||||||
label: 'API Key',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: 'API key of your Appwrite project.',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'instanceUrl',
|
|
||||||
label: 'Appwrite instance URL',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
readOnly: false,
|
|
||||||
placeholder: '',
|
|
||||||
description: '',
|
|
||||||
clickToCopy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'host',
|
|
||||||
label: 'Host Name',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: 'Host name of your Appwrite project.',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
};
|
|
@@ -1,5 +0,0 @@
|
|||||||
const verifyCredentials = async ($) => {
|
|
||||||
await $.http.get('/v1/users');
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
@@ -1,16 +0,0 @@
|
|||||||
const addAuthHeader = ($, requestConfig) => {
|
|
||||||
requestConfig.headers['Content-Type'] = 'application/json';
|
|
||||||
|
|
||||||
if ($.auth.data?.apiKey && $.auth.data?.projectId) {
|
|
||||||
requestConfig.headers['X-Appwrite-Project'] = $.auth.data.projectId;
|
|
||||||
requestConfig.headers['X-Appwrite-Key'] = $.auth.data.apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($.auth.data?.host) {
|
|
||||||
requestConfig.headers['Host'] = $.auth.data.host;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -1,13 +0,0 @@
|
|||||||
const setBaseUrl = ($, requestConfig) => {
|
|
||||||
const instanceUrl = $.auth.data.instanceUrl;
|
|
||||||
|
|
||||||
if (instanceUrl) {
|
|
||||||
requestConfig.baseURL = instanceUrl;
|
|
||||||
} else if ($.app.apiBaseUrl) {
|
|
||||||
requestConfig.baseURL = $.app.apiBaseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default setBaseUrl;
|
|
@@ -1,4 +0,0 @@
|
|||||||
import listCollections from './list-collections/index.js';
|
|
||||||
import listDatabases from './list-databases/index.js';
|
|
||||||
|
|
||||||
export default [listCollections, listDatabases];
|
|
@@ -1,44 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List collections',
|
|
||||||
key: 'listCollections',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const collections = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const databaseId = $.step.parameters.databaseId;
|
|
||||||
|
|
||||||
if (!databaseId) {
|
|
||||||
return collections;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
queries: [
|
|
||||||
JSON.stringify({
|
|
||||||
method: 'orderAsc',
|
|
||||||
attribute: 'name',
|
|
||||||
}),
|
|
||||||
JSON.stringify({
|
|
||||||
method: 'limit',
|
|
||||||
values: [100],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.get(
|
|
||||||
`/v1/databases/${databaseId}/collections`,
|
|
||||||
{ params }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (data?.collections) {
|
|
||||||
for (const collection of data.collections) {
|
|
||||||
collections.data.push({
|
|
||||||
value: collection.$id,
|
|
||||||
name: collection.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return collections;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,36 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List databases',
|
|
||||||
key: 'listDatabases',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const databases = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
queries: [
|
|
||||||
JSON.stringify({
|
|
||||||
method: 'orderAsc',
|
|
||||||
attribute: 'name',
|
|
||||||
}),
|
|
||||||
JSON.stringify({
|
|
||||||
method: 'limit',
|
|
||||||
values: [100],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.get('/v1/databases', { params });
|
|
||||||
|
|
||||||
if (data?.databases) {
|
|
||||||
for (const database of data.databases) {
|
|
||||||
databases.data.push({
|
|
||||||
value: database.$id,
|
|
||||||
name: database.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return databases;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,21 +0,0 @@
|
|||||||
import defineApp from '../../helpers/define-app.js';
|
|
||||||
import addAuthHeader from './common/add-auth-header.js';
|
|
||||||
import setBaseUrl from './common/set-base-url.js';
|
|
||||||
import auth from './auth/index.js';
|
|
||||||
import triggers from './triggers/index.js';
|
|
||||||
import dynamicData from './dynamic-data/index.js';
|
|
||||||
|
|
||||||
export default defineApp({
|
|
||||||
name: 'Appwrite',
|
|
||||||
key: 'appwrite',
|
|
||||||
baseUrl: 'https://appwrite.io',
|
|
||||||
apiBaseUrl: 'https://cloud.appwrite.io',
|
|
||||||
iconUrl: '{BASE_URL}/apps/appwrite/assets/favicon.svg',
|
|
||||||
authDocUrl: '{DOCS_URL}/apps/appwrite/connection',
|
|
||||||
primaryColor: '#FD366E',
|
|
||||||
supportsConnections: true,
|
|
||||||
beforeRequest: [setBaseUrl, addAuthHeader],
|
|
||||||
auth,
|
|
||||||
triggers,
|
|
||||||
dynamicData,
|
|
||||||
});
|
|
@@ -1,3 +0,0 @@
|
|||||||
import newDocuments from './new-documents/index.js';
|
|
||||||
|
|
||||||
export default [newDocuments];
|
|
@@ -1,104 +0,0 @@
|
|||||||
import defineTrigger from '../../../../helpers/define-trigger.js';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New documents',
|
|
||||||
key: 'newDocuments',
|
|
||||||
pollInterval: 15,
|
|
||||||
description: 'Triggers when a new document is created.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Database',
|
|
||||||
key: 'databaseId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listDatabases',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Collection',
|
|
||||||
key: 'collectionId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.databaseId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listCollections',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.databaseId',
|
|
||||||
value: '{parameters.databaseId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const { databaseId, collectionId } = $.step.parameters;
|
|
||||||
|
|
||||||
const limit = 1;
|
|
||||||
let lastDocumentId = undefined;
|
|
||||||
let offset = 0;
|
|
||||||
let documentCount = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
const params = {
|
|
||||||
queries: [
|
|
||||||
JSON.stringify({
|
|
||||||
method: 'orderDesc',
|
|
||||||
attribute: '$createdAt',
|
|
||||||
}),
|
|
||||||
JSON.stringify({
|
|
||||||
method: 'limit',
|
|
||||||
values: [limit],
|
|
||||||
}),
|
|
||||||
// An invalid cursor shouldn't be sent.
|
|
||||||
lastDocumentId &&
|
|
||||||
JSON.stringify({
|
|
||||||
method: 'cursorAfter',
|
|
||||||
values: [lastDocumentId],
|
|
||||||
}),
|
|
||||||
].filter(Boolean),
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.get(
|
|
||||||
`/v1/databases/${databaseId}/collections/${collectionId}/documents`,
|
|
||||||
{ params }
|
|
||||||
);
|
|
||||||
|
|
||||||
const documents = data?.documents;
|
|
||||||
documentCount = documents?.length;
|
|
||||||
offset = offset + limit;
|
|
||||||
lastDocumentId = documents[documentCount - 1]?.$id;
|
|
||||||
|
|
||||||
if (!documentCount) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const document of documents) {
|
|
||||||
$.pushTriggerItem({
|
|
||||||
raw: document,
|
|
||||||
meta: {
|
|
||||||
internalId: document.$id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} while (documentCount === limit);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -11,8 +11,8 @@ export default defineApp({
|
|||||||
'https://azure.microsoft.com/en-us/products/ai-services/openai-service',
|
'https://azure.microsoft.com/en-us/products/ai-services/openai-service',
|
||||||
apiBaseUrl: '',
|
apiBaseUrl: '',
|
||||||
iconUrl: '{BASE_URL}/apps/azure-openai/assets/favicon.svg',
|
iconUrl: '{BASE_URL}/apps/azure-openai/assets/favicon.svg',
|
||||||
authDocUrl: '{DOCS_URL}/apps/azure-openai/connection',
|
authDocUrl: 'https://automatisch.io/docs/apps/azure-openai/connection',
|
||||||
primaryColor: '#000000',
|
primaryColor: '000000',
|
||||||
supportsConnections: true,
|
supportsConnections: true,
|
||||||
beforeRequest: [setBaseUrl, addAuthHeader],
|
beforeRequest: [setBaseUrl, addAuthHeader],
|
||||||
auth,
|
auth,
|
||||||
|
@@ -7,7 +7,7 @@ export default defineAction({
|
|||||||
'Creates an attachment of a specified object by given parent ID.',
|
'Creates an attachment of a specified object by given parent ID.',
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
{
|
||||||
label: 'Template Data',
|
label: 'Templete Data',
|
||||||
key: 'templateData',
|
key: 'templateData',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
|
@@ -7,11 +7,11 @@ export default defineApp({
|
|||||||
name: 'Carbone',
|
name: 'Carbone',
|
||||||
key: 'carbone',
|
key: 'carbone',
|
||||||
iconUrl: '{BASE_URL}/apps/carbone/assets/favicon.svg',
|
iconUrl: '{BASE_URL}/apps/carbone/assets/favicon.svg',
|
||||||
authDocUrl: '{DOCS_URL}/apps/carbone/connection',
|
authDocUrl: 'https://automatisch.io/docs/apps/carbone/connection',
|
||||||
supportsConnections: true,
|
supportsConnections: true,
|
||||||
baseUrl: 'https://carbone.io',
|
baseUrl: 'https://carbone.io',
|
||||||
apiBaseUrl: 'https://api.carbone.io',
|
apiBaseUrl: 'https://api.carbone.io',
|
||||||
primaryColor: '#6f42c1',
|
primaryColor: '6f42c1',
|
||||||
beforeRequest: [addAuthHeader],
|
beforeRequest: [addAuthHeader],
|
||||||
auth,
|
auth,
|
||||||
actions,
|
actions,
|
||||||
|
@@ -1,72 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Create folder',
|
|
||||||
key: 'createFolder',
|
|
||||||
description: 'Creates a new folder.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Workspace',
|
|
||||||
key: 'workspaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listWorkspaces',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Space',
|
|
||||||
key: 'spaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.workspaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listSpaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.workspaceId',
|
|
||||||
value: '{parameters.workspaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Folder Name',
|
|
||||||
key: 'folderName',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const { spaceId, folderName } = $.step.parameters;
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
name: folderName,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.post(`/v2/space/${spaceId}/folder`, body);
|
|
||||||
|
|
||||||
$.setActionItem({
|
|
||||||
raw: data,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,135 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Create list',
|
|
||||||
key: 'createList',
|
|
||||||
description: 'Creates a new list.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Workspace',
|
|
||||||
key: 'workspaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listWorkspaces',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Space',
|
|
||||||
key: 'spaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.workspaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listSpaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.workspaceId',
|
|
||||||
value: '{parameters.workspaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Folder',
|
|
||||||
key: 'folderId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.spaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listFolders',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.spaceId',
|
|
||||||
value: '{parameters.spaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'List Name',
|
|
||||||
key: 'listName',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'List Info',
|
|
||||||
key: 'listInfo',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Priority',
|
|
||||||
key: 'priority',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
options: [
|
|
||||||
{ label: 'Urgent', value: 1 },
|
|
||||||
{ label: 'High', value: 2 },
|
|
||||||
{ label: 'Normal', value: 3 },
|
|
||||||
{ label: 'Low', value: 4 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Due Date',
|
|
||||||
key: 'dueDate',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
description: 'format: integer <int64>',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const { folderId, listName, listInfo, priority, dueDate } =
|
|
||||||
$.step.parameters;
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
name: listName,
|
|
||||||
content: listInfo,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (priority) {
|
|
||||||
body.priority = priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dueDate) {
|
|
||||||
body.due_date = dueDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.post(`/v2/folder/${folderId}/list`, body);
|
|
||||||
|
|
||||||
$.setActionItem({
|
|
||||||
raw: data,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,294 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Create task',
|
|
||||||
key: 'createTask',
|
|
||||||
description: 'Creates a new task.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Workspace',
|
|
||||||
key: 'workspaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listWorkspaces',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Space',
|
|
||||||
key: 'spaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.workspaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listSpaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.workspaceId',
|
|
||||||
value: '{parameters.workspaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Folder',
|
|
||||||
key: 'folderId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.spaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listFolders',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.spaceId',
|
|
||||||
value: '{parameters.spaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'List',
|
|
||||||
key: 'listId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
dependsOn: ['parameters.folderId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listLists',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.folderId',
|
|
||||||
value: '{parameters.folderId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Task Name',
|
|
||||||
key: 'taskName',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Task Description',
|
|
||||||
key: 'taskDescription',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Markdown Content',
|
|
||||||
key: 'markdownContent',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
options: [
|
|
||||||
{ label: 'False', value: 'false' },
|
|
||||||
{ label: 'True', value: 'true' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Assignees',
|
|
||||||
key: 'assigneeIds',
|
|
||||||
type: 'dynamic',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
label: 'Assignee',
|
|
||||||
key: 'assigneeId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.listId'],
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listAssignees',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.listId',
|
|
||||||
value: '{parameters.listId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Task Status',
|
|
||||||
key: 'taskStatus',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.listId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listStatuses',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.listId',
|
|
||||||
value: '{parameters.listId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Tags',
|
|
||||||
key: 'tagIds',
|
|
||||||
type: 'dynamic',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
label: 'tag',
|
|
||||||
key: 'tagId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listTags',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Priority',
|
|
||||||
key: 'priority',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
options: [
|
|
||||||
{ label: 'Urgent', value: 1 },
|
|
||||||
{ label: 'High', value: 2 },
|
|
||||||
{ label: 'Normal', value: 3 },
|
|
||||||
{ label: 'Low', value: 4 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Due Date',
|
|
||||||
key: 'dueDate',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
description: 'format: integer <int64>',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Start Date',
|
|
||||||
key: 'startDate',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
description: 'format: integer <int64>',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const {
|
|
||||||
listId,
|
|
||||||
taskName,
|
|
||||||
taskDescription,
|
|
||||||
markdownContent,
|
|
||||||
assigneeIds,
|
|
||||||
taskStatus,
|
|
||||||
tagIds,
|
|
||||||
priority,
|
|
||||||
dueDate,
|
|
||||||
startDate,
|
|
||||||
} = $.step.parameters;
|
|
||||||
|
|
||||||
const tags = tagIds.map((tag) => tag.tagId);
|
|
||||||
const assignees = assigneeIds.map((assignee) =>
|
|
||||||
Number(assignee.assigneeId)
|
|
||||||
);
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
name: taskName,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (assignees.length) {
|
|
||||||
body.assignees = assignees;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (taskStatus) {
|
|
||||||
body.status = taskStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tags.length) {
|
|
||||||
body.tags = tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priority) {
|
|
||||||
body.priority = priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dueDate) {
|
|
||||||
body.due_date = dueDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startDate) {
|
|
||||||
body.start_date = startDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (markdownContent) {
|
|
||||||
body.markdown_description = taskDescription;
|
|
||||||
} else {
|
|
||||||
body.description = taskDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.post(`/v2/list/${listId}/task`, body);
|
|
||||||
|
|
||||||
$.setActionItem({
|
|
||||||
raw: data,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,82 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Find task by id',
|
|
||||||
key: 'findTaskById',
|
|
||||||
description: 'Finds a task using id.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Task ID',
|
|
||||||
key: 'taskId',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Use Custom ID',
|
|
||||||
key: 'useCustomId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'True',
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'False',
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
additionalFields: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicFields',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listFieldsWhenUsingCustomId',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.useCustomId',
|
|
||||||
value: '{parameters.useCustomId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Include Subtasks?',
|
|
||||||
key: 'includeSubtasks',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'True',
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'False',
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const { taskId, useCustomId, includeSubtasks } = $.step.parameters;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
custom_task_ids: useCustomId || false,
|
|
||||||
include_subtasks: includeSubtasks,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.get(`/v2/task/${taskId}`, { params });
|
|
||||||
|
|
||||||
$.setActionItem({
|
|
||||||
raw: data,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,6 +0,0 @@
|
|||||||
import createFolder from './create-folder/index.js';
|
|
||||||
import createList from './create-list/index.js';
|
|
||||||
import createTask from './create-task/index.js';
|
|
||||||
import findTaskById from './find-task-by-id/index.js';
|
|
||||||
|
|
||||||
export default [createFolder, createList, createTask, findTaskById];
|
|
@@ -1,27 +0,0 @@
|
|||||||
<svg width="185" height="185" viewBox="0 0 185 185" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g filter="url(#filter0_d)">
|
|
||||||
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
|
|
||||||
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.8789 105.714L69.3974 95.3593C76.5762 104.732 84.1998 109.051 92.6948 109.051C101.143 109.051 108.557 104.781 115.414 95.4832L129.119 105.59C119.232 118.996 106.932 126.079 92.6948 126.079C78.5049 126.079 66.0907 119.046 55.8789 105.714Z" fill="url(#paint0_linear)"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M92.6491 60.7078L68.5883 81.4406L57.4727 68.5407L92.6969 38.1885L127.647 68.5644L116.477 81.417L92.6491 60.7078Z" fill="url(#paint1_linear)"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<filter id="filter0_d" x="0" y="0" width="185" height="185" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
|
||||||
<feOffset dy="10"/>
|
|
||||||
<feGaussianBlur stdDeviation="15"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.117647 0 0 0 0 0.211765 0 0 0 0.1 0"/>
|
|
||||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
|
||||||
</filter>
|
|
||||||
<linearGradient id="paint0_linear" x1="55.8789" y1="116.251" x2="129.119" y2="116.251" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#8930FD"/>
|
|
||||||
<stop offset="1" stop-color="#49CCF9"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint1_linear" x1="57.4727" y1="67.6025" x2="127.647" y2="67.6025" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#FF02F0"/>
|
|
||||||
<stop offset="1" stop-color="#FFC800"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,21 +0,0 @@
|
|||||||
import { URLSearchParams } from 'url';
|
|
||||||
|
|
||||||
export default async function generateAuthUrl($) {
|
|
||||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
|
||||||
(field) => field.key == 'oAuthRedirectUrl'
|
|
||||||
);
|
|
||||||
const redirectUri = oauthRedirectUrlField.value;
|
|
||||||
const state = Math.random().toString();
|
|
||||||
const searchParams = new URLSearchParams({
|
|
||||||
client_id: $.auth.data.clientId,
|
|
||||||
redirect_uri: redirectUri,
|
|
||||||
state,
|
|
||||||
});
|
|
||||||
|
|
||||||
const url = `https://app.clickup.com/api?${searchParams.toString()}`;
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
url,
|
|
||||||
originalState: state,
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,46 +0,0 @@
|
|||||||
import generateAuthUrl from './generate-auth-url.js';
|
|
||||||
import verifyCredentials from './verify-credentials.js';
|
|
||||||
import isStillVerified from './is-still-verified.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
key: 'oAuthRedirectUrl',
|
|
||||||
label: 'OAuth Redirect URL',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: true,
|
|
||||||
value: '{WEB_APP_URL}/app/clickup/connections/add',
|
|
||||||
placeholder: null,
|
|
||||||
description:
|
|
||||||
'When asked to input a redirect URL in ClickUp, enter the URL above.',
|
|
||||||
clickToCopy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'clientId',
|
|
||||||
label: 'Client ID',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'clientSecret',
|
|
||||||
label: 'Client Secret',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
generateAuthUrl,
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
};
|
|
@@ -1,8 +0,0 @@
|
|||||||
import getCurrentUser from '../common/get-current-user.js';
|
|
||||||
|
|
||||||
const isStillVerified = async ($) => {
|
|
||||||
const currentUser = await getCurrentUser($);
|
|
||||||
return !!currentUser.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default isStillVerified;
|
|
@@ -1,31 +0,0 @@
|
|||||||
import getCurrentUser from '../common/get-current-user.js';
|
|
||||||
|
|
||||||
const verifyCredentials = async ($) => {
|
|
||||||
if ($.auth.data.originalState !== $.auth.data.state) {
|
|
||||||
throw new Error(`The 'state' parameter does not match.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.post('/v2/oauth/token', {
|
|
||||||
client_id: $.auth.data.clientId,
|
|
||||||
client_secret: $.auth.data.clientSecret,
|
|
||||||
code: $.auth.data.code,
|
|
||||||
});
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
accessToken: data.access_token,
|
|
||||||
tokenType: data.token_type,
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentUser = await getCurrentUser($);
|
|
||||||
const screenName = [currentUser.username, currentUser.email]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join(' @ ');
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
clientId: $.auth.data.clientId,
|
|
||||||
clientSecret: $.auth.data.clientSecret,
|
|
||||||
screenName,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
@@ -1,9 +0,0 @@
|
|||||||
const addAuthHeader = ($, requestConfig) => {
|
|
||||||
if ($.auth.data?.accessToken) {
|
|
||||||
requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -1,6 +0,0 @@
|
|||||||
const getCurrentUser = async ($) => {
|
|
||||||
const { data } = await $.http.get('/v2/user');
|
|
||||||
return data.user;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getCurrentUser;
|
|
@@ -1,19 +0,0 @@
|
|||||||
import listAssignees from './list-assignees/index.js';
|
|
||||||
import listFolders from './list-folders/index.js';
|
|
||||||
import listLists from './list-lists/index.js';
|
|
||||||
import listSpaces from './list-spaces/index.js';
|
|
||||||
import listStatuses from './list-statuses/index.js';
|
|
||||||
import listTags from './list-tags/index.js';
|
|
||||||
import listTasks from './list-tasks/index.js';
|
|
||||||
import listWorkspaces from './list-workspaces/index.js';
|
|
||||||
|
|
||||||
export default [
|
|
||||||
listAssignees,
|
|
||||||
listFolders,
|
|
||||||
listLists,
|
|
||||||
listSpaces,
|
|
||||||
listStatuses,
|
|
||||||
listTags,
|
|
||||||
listTasks,
|
|
||||||
listWorkspaces,
|
|
||||||
];
|
|
@@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List assignees',
|
|
||||||
key: 'listAssignees',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const assignees = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const listId = $.step.parameters.listId;
|
|
||||||
|
|
||||||
if (!listId) {
|
|
||||||
return assignees;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.get(`/v2/list/${listId}/member`);
|
|
||||||
|
|
||||||
if (data.members) {
|
|
||||||
for (const member of data.members) {
|
|
||||||
assignees.data.push({
|
|
||||||
value: member.id,
|
|
||||||
name: member.username,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return assignees;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List folders',
|
|
||||||
key: 'listFolders',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const folders = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const spaceId = $.step.parameters.spaceId;
|
|
||||||
|
|
||||||
if (!spaceId) {
|
|
||||||
return folders;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.get(`/v2/space/${spaceId}/folder`);
|
|
||||||
|
|
||||||
if (data.folders) {
|
|
||||||
for (const folder of data.folders) {
|
|
||||||
folders.data.push({
|
|
||||||
value: folder.id,
|
|
||||||
name: folder.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return folders;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List lists',
|
|
||||||
key: 'listLists',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const lists = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const folderId = $.step.parameters.folderId;
|
|
||||||
|
|
||||||
if (!folderId) {
|
|
||||||
return lists;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.get(`/v2/folder/${folderId}/list`);
|
|
||||||
|
|
||||||
if (data.lists) {
|
|
||||||
for (const list of data.lists) {
|
|
||||||
lists.data.push({
|
|
||||||
value: list.id,
|
|
||||||
name: list.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lists;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List spaces',
|
|
||||||
key: 'listSpaces',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const spaces = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const workspaceId = $.step.parameters.workspaceId;
|
|
||||||
|
|
||||||
if (!workspaceId) {
|
|
||||||
return spaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.get(`/v2/team/${workspaceId}/space`);
|
|
||||||
|
|
||||||
if (data.spaces) {
|
|
||||||
for (const space of data.spaces) {
|
|
||||||
spaces.data.push({
|
|
||||||
value: space.id,
|
|
||||||
name: space.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return spaces;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List statuses',
|
|
||||||
key: 'listStatuses',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const statuses = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const listId = $.step.parameters.listId;
|
|
||||||
|
|
||||||
if (!listId) {
|
|
||||||
return statuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.get(`/v2/list/${listId}`);
|
|
||||||
|
|
||||||
if (data.statuses) {
|
|
||||||
for (const status of data.statuses) {
|
|
||||||
statuses.data.push({
|
|
||||||
value: status.status,
|
|
||||||
name: status.status,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return statuses;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List tags',
|
|
||||||
key: 'listTags',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const tags = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const spaceId = $.step.parameters.spaceId;
|
|
||||||
|
|
||||||
if (!spaceId) {
|
|
||||||
return spaceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.get(`v2/space/${spaceId}/tag`);
|
|
||||||
|
|
||||||
if (data.tags) {
|
|
||||||
for (const tag of data.tags) {
|
|
||||||
tags.data.push({
|
|
||||||
value: tag.name,
|
|
||||||
name: tag.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tags;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,41 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List tasks',
|
|
||||||
key: 'listTasks',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const tasks = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
const listId = $.step.parameters.listId;
|
|
||||||
let next = false;
|
|
||||||
|
|
||||||
if (!listId) {
|
|
||||||
return tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
order_by: 'created',
|
|
||||||
reverse: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
do {
|
|
||||||
const { data } = await $.http.get(`/v2/list/${listId}/task`, { params });
|
|
||||||
if (data.last_page) {
|
|
||||||
next = false;
|
|
||||||
} else {
|
|
||||||
next = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.tasks) {
|
|
||||||
for (const task of data.tasks) {
|
|
||||||
tasks.data.push({
|
|
||||||
value: task.id,
|
|
||||||
name: task.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (next);
|
|
||||||
|
|
||||||
return tasks;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,23 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List workspaces',
|
|
||||||
key: 'listWorkspaces',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const workspaces = {
|
|
||||||
data: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.get('/v2/team');
|
|
||||||
|
|
||||||
if (data.teams) {
|
|
||||||
for (const workspace of data.teams) {
|
|
||||||
workspaces.data.push({
|
|
||||||
value: workspace.id,
|
|
||||||
name: workspace.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return workspaces;
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,3 +0,0 @@
|
|||||||
import useCustomId from './use-custom-id/index.js';
|
|
||||||
|
|
||||||
export default [useCustomId];
|
|
@@ -1,29 +0,0 @@
|
|||||||
export default {
|
|
||||||
name: 'List workspaces when using custom id',
|
|
||||||
key: 'listFieldsWhenUsingCustomId',
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
if ($.step.parameters.useCustomId) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: 'Workspace',
|
|
||||||
key: 'workspaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listWorkspaces',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,24 +0,0 @@
|
|||||||
import defineApp from '../../helpers/define-app.js';
|
|
||||||
import addAuthHeader from './common/add-auth-header.js';
|
|
||||||
import auth from './auth/index.js';
|
|
||||||
import triggers from './triggers/index.js';
|
|
||||||
import dynamicData from './dynamic-data/index.js';
|
|
||||||
import actions from './actions/index.js';
|
|
||||||
import dynamicFields from './dynamic-fields/index.js';
|
|
||||||
|
|
||||||
export default defineApp({
|
|
||||||
name: 'ClickUp',
|
|
||||||
key: 'clickup',
|
|
||||||
baseUrl: 'https://clickup.com',
|
|
||||||
apiBaseUrl: 'https://api.clickup.com/api',
|
|
||||||
iconUrl: '{BASE_URL}/apps/clickup/assets/favicon.svg',
|
|
||||||
authDocUrl: '{DOCS_URL}/apps/clickup/connection',
|
|
||||||
primaryColor: '#FD71AF',
|
|
||||||
supportsConnections: true,
|
|
||||||
beforeRequest: [addAuthHeader],
|
|
||||||
auth,
|
|
||||||
triggers,
|
|
||||||
dynamicData,
|
|
||||||
actions,
|
|
||||||
dynamicFields,
|
|
||||||
});
|
|
@@ -1,6 +0,0 @@
|
|||||||
import newFolders from './new-folders/index.js';
|
|
||||||
import newLists from './new-lists/index.js';
|
|
||||||
import newTasks from './new-tasks/index.js';
|
|
||||||
import updatedTask from './updated-task/index.js';
|
|
||||||
|
|
||||||
export default [newFolders, newLists, newTasks, updatedTask];
|
|
@@ -1,105 +0,0 @@
|
|||||||
import Crypto from 'crypto';
|
|
||||||
import defineTrigger from '../../../../helpers/define-trigger.js';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New folders',
|
|
||||||
key: 'newFolder',
|
|
||||||
type: 'webhook',
|
|
||||||
description: 'Triggers when a new folder is created.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Workspace',
|
|
||||||
key: 'workspaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listWorkspaces',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Space',
|
|
||||||
key: 'spaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.workspaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listSpaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.workspaceId',
|
|
||||||
value: '{parameters.workspaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const dataItem = {
|
|
||||||
raw: $.request.body,
|
|
||||||
meta: {
|
|
||||||
internalId: $.request.body.folder_id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
async testRun($) {
|
|
||||||
const sampleEventData = {
|
|
||||||
event: 'folderCreated',
|
|
||||||
folder_id: '90180382912',
|
|
||||||
webhook_id: Crypto.randomUUID(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const dataItem = {
|
|
||||||
raw: sampleEventData,
|
|
||||||
meta: {
|
|
||||||
internalId: '',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
async registerHook($) {
|
|
||||||
const { workspaceId, spaceId } = $.step.parameters;
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
name: $.flow.id,
|
|
||||||
endpoint: $.webhookUrl,
|
|
||||||
events: ['folderCreated'],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (spaceId) {
|
|
||||||
payload.space_id = spaceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.post(
|
|
||||||
`/v2/team/${workspaceId}/webhook`,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
|
|
||||||
await $.flow.setRemoteWebhookId(data.id);
|
|
||||||
},
|
|
||||||
|
|
||||||
async unregisterHook($) {
|
|
||||||
await $.http.delete(`/v2/webhook/${$.flow.remoteWebhookId}`);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,129 +0,0 @@
|
|||||||
import Crypto from 'crypto';
|
|
||||||
import defineTrigger from '../../../../helpers/define-trigger.js';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New lists',
|
|
||||||
key: 'newLists',
|
|
||||||
type: 'webhook',
|
|
||||||
description: 'Triggers when a new list is created.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Workspace',
|
|
||||||
key: 'workspaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listWorkspaces',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Space',
|
|
||||||
key: 'spaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.workspaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listSpaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.workspaceId',
|
|
||||||
value: '{parameters.workspaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Folder',
|
|
||||||
key: 'folderId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.spaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listFolders',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.spaceId',
|
|
||||||
value: '{parameters.spaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const dataItem = {
|
|
||||||
raw: $.request.body,
|
|
||||||
meta: {
|
|
||||||
internalId: $.request.body.list_id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
async testRun($) {
|
|
||||||
const sampleEventData = {
|
|
||||||
event: 'listCreated',
|
|
||||||
list_id: '901800588812',
|
|
||||||
webhook_id: Crypto.randomUUID(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const dataItem = {
|
|
||||||
raw: sampleEventData,
|
|
||||||
meta: {
|
|
||||||
internalId: sampleEventData.webhook_id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
async registerHook($) {
|
|
||||||
const { workspaceId, spaceId, folderId } = $.step.parameters;
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
name: $.flow.id,
|
|
||||||
endpoint: $.webhookUrl,
|
|
||||||
events: ['listCreated'],
|
|
||||||
space_id: spaceId,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (folderId) {
|
|
||||||
payload.folder_id = folderId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.post(
|
|
||||||
`/v2/team/${workspaceId}/webhook`,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
|
|
||||||
await $.flow.setRemoteWebhookId(data.id);
|
|
||||||
},
|
|
||||||
|
|
||||||
async unregisterHook($) {
|
|
||||||
await $.http.delete(`/v2/webhook/${$.flow.remoteWebhookId}`);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,186 +0,0 @@
|
|||||||
import Crypto from 'crypto';
|
|
||||||
import defineTrigger from '../../../../helpers/define-trigger.js';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'New tasks',
|
|
||||||
key: 'newTasks',
|
|
||||||
type: 'webhook',
|
|
||||||
description: 'Triggers when a new task is created.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Workspace',
|
|
||||||
key: 'workspaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listWorkspaces',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Space',
|
|
||||||
key: 'spaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.workspaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listSpaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.workspaceId',
|
|
||||||
value: '{parameters.workspaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Folder',
|
|
||||||
key: 'folderId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.spaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listFolders',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.spaceId',
|
|
||||||
value: '{parameters.spaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'List',
|
|
||||||
key: 'listId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.folderId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listLists',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.folderId',
|
|
||||||
value: '{parameters.folderId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Task',
|
|
||||||
key: 'taskId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.listId'],
|
|
||||||
description:
|
|
||||||
'Choose an optional task to determine when this flow should be activated. In this scenario, only subtasks will initiate this flow.',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listTasks',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.listId',
|
|
||||||
value: '{parameters.listId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const dataItem = {
|
|
||||||
raw: $.request.body,
|
|
||||||
meta: {
|
|
||||||
internalId: $.request.body.task_id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
async testRun($) {
|
|
||||||
const sampleEventData = {
|
|
||||||
event: 'taskCreated',
|
|
||||||
task_id: '86enn7pg7',
|
|
||||||
webhook_id: Crypto.randomUUID(),
|
|
||||||
history_items: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const dataItem = {
|
|
||||||
raw: sampleEventData,
|
|
||||||
meta: {
|
|
||||||
internalId: sampleEventData.webhook_id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
async registerHook($) {
|
|
||||||
const { workspaceId, spaceId, folderId, listId, taskId } =
|
|
||||||
$.step.parameters;
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
name: $.flow.id,
|
|
||||||
endpoint: $.webhookUrl,
|
|
||||||
events: ['taskCreated'],
|
|
||||||
space_id: spaceId,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (folderId) {
|
|
||||||
payload.folder_id = folderId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listId) {
|
|
||||||
payload.list_id = listId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (taskId) {
|
|
||||||
payload.task_id = taskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.post(
|
|
||||||
`/v2/team/${workspaceId}/webhook`,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
|
|
||||||
await $.flow.setRemoteWebhookId(data.id);
|
|
||||||
},
|
|
||||||
|
|
||||||
async unregisterHook($) {
|
|
||||||
await $.http.delete(`/v2/webhook/${$.flow.remoteWebhookId}`);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,172 +0,0 @@
|
|||||||
import Crypto from 'crypto';
|
|
||||||
import defineTrigger from '../../../../helpers/define-trigger.js';
|
|
||||||
|
|
||||||
export default defineTrigger({
|
|
||||||
name: 'Updated task',
|
|
||||||
key: 'updatedTask',
|
|
||||||
type: 'webhook',
|
|
||||||
description: 'Triggers when a task is updated.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Workspace',
|
|
||||||
key: 'workspaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: true,
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listWorkspaces',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Space',
|
|
||||||
key: 'spaceId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.workspaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listSpaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.workspaceId',
|
|
||||||
value: '{parameters.workspaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Folder',
|
|
||||||
key: 'folderId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.spaceId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listFolders',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.spaceId',
|
|
||||||
value: '{parameters.spaceId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'List',
|
|
||||||
key: 'listId',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
dependsOn: ['parameters.folderId'],
|
|
||||||
description: '',
|
|
||||||
variables: true,
|
|
||||||
source: {
|
|
||||||
type: 'query',
|
|
||||||
name: 'getDynamicData',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'key',
|
|
||||||
value: 'listLists',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parameters.folderId',
|
|
||||||
value: '{parameters.folderId}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'What Changed?',
|
|
||||||
key: 'whatChanged',
|
|
||||||
type: 'dropdown',
|
|
||||||
required: false,
|
|
||||||
variables: true,
|
|
||||||
options: [
|
|
||||||
{ label: 'Status', value: 'taskStatusUpdated' },
|
|
||||||
{ label: 'Assignee Added', value: 'taskAssigneeUpdated' },
|
|
||||||
{ label: 'Priority', value: 'taskPriorityUpdated' },
|
|
||||||
{ label: 'Tag Added', value: 'taskTagUpdated' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const dataItem = {
|
|
||||||
raw: $.request.body,
|
|
||||||
meta: {
|
|
||||||
internalId: Crypto.randomUUID(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
async testRun($) {
|
|
||||||
const sampleEventData = {
|
|
||||||
event: 'taskUpdated',
|
|
||||||
task_id: '86enn7pg7',
|
|
||||||
webhook_id: Crypto.randomUUID(),
|
|
||||||
history_items: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const dataItem = {
|
|
||||||
raw: sampleEventData,
|
|
||||||
meta: {
|
|
||||||
internalId: sampleEventData.webhook_id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
$.pushTriggerItem(dataItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
async registerHook($) {
|
|
||||||
const { workspaceId, spaceId, folderId, listId, whatChanged } =
|
|
||||||
$.step.parameters;
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
name: $.flow.id,
|
|
||||||
endpoint: $.webhookUrl,
|
|
||||||
space_id: spaceId,
|
|
||||||
};
|
|
||||||
|
|
||||||
payload.events = [whatChanged || 'taskUpdated'];
|
|
||||||
|
|
||||||
if (folderId) {
|
|
||||||
payload.folder_id = folderId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listId) {
|
|
||||||
payload.list_id = listId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await $.http.post(
|
|
||||||
`/v2/team/${workspaceId}/webhook`,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
|
|
||||||
await $.flow.setRemoteWebhookId(data.id);
|
|
||||||
},
|
|
||||||
|
|
||||||
async unregisterHook($) {
|
|
||||||
await $.http.delete(`/v2/webhook/${$.flow.remoteWebhookId}`);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,3 +0,0 @@
|
|||||||
import runJavascript from './run-javascript/index.js';
|
|
||||||
|
|
||||||
export default [runJavascript];
|
|
@@ -1,84 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Run Javascript',
|
|
||||||
key: 'runJavascript',
|
|
||||||
description:
|
|
||||||
'Run browser Javascript code. You can not use NodeJS specific features and npm packages.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Inputs',
|
|
||||||
key: 'inputs',
|
|
||||||
type: 'dynamic',
|
|
||||||
required: false,
|
|
||||||
description:
|
|
||||||
'To be able to use data from previous steps, you need to expose them as input entries. You can access these input values in your code by using the `inputs` argument.',
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
key: '',
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
label: 'Key',
|
|
||||||
key: 'key',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
variables: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Value',
|
|
||||||
key: 'value',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
variables: true,
|
|
||||||
valueType: 'parse',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Code Snippet',
|
|
||||||
key: 'codeSnippet',
|
|
||||||
type: 'code',
|
|
||||||
required: true,
|
|
||||||
variables: false,
|
|
||||||
value:
|
|
||||||
'const code = async (inputs) => { \n // E.g. if you have an input called username,\n // you can access its value by calling inputs.username\n // Return value will be used as output of this step.\n\n return true;\n};',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const { inputs = [], codeSnippet } = $.step.parameters;
|
|
||||||
|
|
||||||
const objectifiedInput = {};
|
|
||||||
for (const input of inputs) {
|
|
||||||
if (input.key) {
|
|
||||||
objectifiedInput[input.key] = input.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ivm = (await import('isolated-vm')).default;
|
|
||||||
const isolate = new ivm.Isolate({ memoryLimit: 128 });
|
|
||||||
|
|
||||||
try {
|
|
||||||
const context = await isolate.createContext();
|
|
||||||
await context.global.set(
|
|
||||||
'inputs',
|
|
||||||
new ivm.ExternalCopy(objectifiedInput).copyInto()
|
|
||||||
);
|
|
||||||
|
|
||||||
const compiledCodeSnippet = await isolate.compileScript(
|
|
||||||
`${codeSnippet}; code(inputs);`
|
|
||||||
);
|
|
||||||
const codeFunction = await compiledCodeSnippet.run(context, {
|
|
||||||
reference: true,
|
|
||||||
promise: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
$.setActionItem({ raw: { output: await codeFunction.copy() } });
|
|
||||||
} finally {
|
|
||||||
isolate.dispose();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user