diff --git a/.all-contributorsrc b/.all-contributorsrc index 09762405a..dfc16ae23 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -762,6 +762,34 @@ "contributions": [ "plugin" ] + }, + { + "login": "la3rence", + "name": "la3rence", + "avatar_url": "https://avatars.githubusercontent.com/u/24540598?v=4", + "profile": "https://lawrenceli.me", + "contributions": [ + "plugin" + ] + }, + { + "login": "dennisrongo", + "name": "Dennis Rongo", + "avatar_url": "https://avatars.githubusercontent.com/u/51771021?v=4", + "profile": "http://dennisrongo.com", + "contributions": [ + "bug" + ] + }, + { + "login": "kartikmehta8", + "name": "Kartik Mehta", + "avatar_url": "https://avatars.githubusercontent.com/u/77505989?v=4", + "profile": "https://github.com/kartikmehta8", + "contributions": [ + "doc", + "code" + ] } ], "commitType": "docs" diff --git a/.github/pre-release-drafter.yml b/.github/pre-release-drafter.yml new file mode 100644 index 000000000..ebfbd7f7e --- /dev/null +++ b/.github/pre-release-drafter.yml @@ -0,0 +1,41 @@ +include-pre-releases: true +exclude-labels: + - 'skip-changelog' + - 'release' + - 'pre-release' +categories: + - title: "โœจ Exciting New Features" + labels: + - "๐ŸŒŸ feature" + - title: "๐Ÿงฉ Pieces" + labels: + - "๐Ÿ”Œ pieces" + - title: "๐Ÿ› ๏ธ Piece Framework" + labels: + - "๐Ÿ› ๏ธ piece-framework" + - title: "๐Ÿž Bug Fixes" + labels: + - "๐Ÿ› bug" + - title: "๐ŸŽจ Enhancements & Polish" + labels: + - "โญ enhancement" + - title: "๐Ÿ“š Documentation" + labels: + - "๐Ÿ“š documentation" + - title: "๐Ÿงน Maintenance" + labels: + - "๐Ÿงน clean up" + - "๐Ÿ’ป tech debt" + - title: "Other Changes" + labels: + - "*" + - title: "๐Ÿ–ฅ๏ธ API Changes" + labels: + - "๐Ÿ–ฅ๏ธ api" + +template: | + + $CHANGES + + ## Thanks โค๏ธ + $CONTRIBUTORS \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 4fde67e1b..647574a0e 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,6 +1,8 @@ -include-pre-releases: true +include-pre-releases: false exclude-labels: - 'skip-changelog' + - 'release' + - 'pre-release' categories: - title: "โœจ Exciting New Features" labels: diff --git a/.github/workflows-backup/build-cloud-nx.yml b/.github/workflows-backup/build-cloud-nx.yml index 4e2ee8e52..771f77f87 100644 --- a/.github/workflows-backup/build-cloud-nx.yml +++ b/.github/workflows-backup/build-cloud-nx.yml @@ -18,7 +18,7 @@ jobs: npx nx-cloud start-ci-run --stop-agents-after="build" --agent-count=3 parallel-commands-on-agents: | npx nx affected --target=lint --parallel=3 - npx nx affected --target=build --parallel=3 + npx nx affected --target=build -c production --parallel=3 npx nx run-many --target=test --projects=engine,shared,backend --parallel=3 agents: diff --git a/.github/workflows-backup/release.yml b/.github/workflows-backup/release.yml index a5ff1ada4..2556c2095 100644 --- a/.github/workflows-backup/release.yml +++ b/.github/workflows-backup/release.yml @@ -44,6 +44,8 @@ jobs: platforms: | linux/amd64 linux/arm64 + linux/arm/v7 + linux/arm/v8 push: true tags: | activepieces/activepieces:${{ env.RELEASE }} diff --git a/.github/workflows/build-cloud-image.yml b/.github/workflows/build-cloud-image.yml index 631fdfab2..290582334 100644 --- a/.github/workflows/build-cloud-image.yml +++ b/.github/workflows/build-cloud-image.yml @@ -6,7 +6,7 @@ on: jobs: Release: env: - MY_VERSION: template3 + MY_VERSION: sign2 runs-on: ubuntu-latest steps: - name: Check out repository code @@ -33,4 +33,4 @@ jobs: linux/amd64 push: true tags: | - ghcr.io/activepieces/activepieces-cloud:${{ env.RELEASE }}.${{ env.MY_VERSION }} \ No newline at end of file + ghcr.io/activepieces/activepieces-cloud:${{ env.RELEASE }}.${{ env.MY_VERSION }} diff --git a/.github/workflows/pre-release-changelog.yml b/.github/workflows/pre-release-changelog.yml new file mode 100644 index 000000000..c60c6e897 --- /dev/null +++ b/.github/workflows/pre-release-changelog.yml @@ -0,0 +1,33 @@ +name: Write Pre Release notes + +on: + pull_request: + types: [opened, reopened, synchronize, edited, closed, labeled] + +permissions: + contents: write + pull-requests: write + +jobs: + Release: + if: contains(github.event.pull_request.labels.*.name, 'pre-release') + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Set RELEASE env var from package.json + run: echo RELEASE=$(node --print "require('./package.json').rcVersion") >> $GITHUB_ENV + + - name: Create release notes + uses: release-drafter/release-drafter@v5 + with: + config-name: pre-release-drafter.yml + commitish: main + prerelease: true + tag: ${{ env.RELEASE }} + name: ${{ env.RELEASE }} + version: ${{ env.RELEASE }} + latest: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index 32a9a467b..4df5e31e0 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -17,16 +17,16 @@ jobs: uses: actions/checkout@v3 - name: Set RELEASE env var from package.json - run: echo RELEASE=$(node --print "require('./package.json').rcVersion") >> $GITHUB_ENV + run: echo RELEASE=$(node --print "require('./package.json').version") >> $GITHUB_ENV - name: Create release notes uses: release-drafter/release-drafter@v5 with: commitish: main - prerelease: true + prerelease: false tag: ${{ env.RELEASE }} name: ${{ env.RELEASE }} version: ${{ env.RELEASE }} - latest: false + latest: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index 684a4edf5..61856268c 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -9,7 +9,7 @@ on: jobs: Release: - if: contains(github.event.pull_request.labels.*.name, 'release') + if: contains(github.event.pull_request.labels.*.name, 'pre-release') runs-on: ubuntu-latest steps: - name: Check out repository code @@ -55,4 +55,4 @@ jobs: push: true tags: | ghcr.io/activepieces/activepieces:${{ env.RELEASE }} - ghcr.io/activepieces/activepieces-cloud:${{ env.CLOUD_RELEASE }} \ No newline at end of file + ghcr.io/activepieces/activepieces-cloud:${{ env.CLOUD_RELEASE }} diff --git a/.gitignore b/.gitignore index 7999e0d1c..5c0d9bcc0 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ testem.log .DS_Store Thumbs.db + .angular .history/ packages/backend/.env diff --git a/.vscode/settings.json b/.vscode/settings.json index a1869fa0e..86f78f83c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,6 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "javascript.preferences.importModuleSpecifier": "project-relative" } diff --git a/Dockerfile b/Dockerfile index 6f4f71f77..75050d4f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ COPY .npmrc package.json package-lock.json ./ RUN npm ci COPY . . -RUN npx nx run-many --target=build --projects=backend,ui-core --skip-nx-cache +RUN npx nx run-many --target=build --projects=backend,ui-core --configuration production --skip-nx-cache # Install backend production dependencies RUN cd dist/packages/backend && npm install --production --force @@ -49,13 +49,14 @@ COPY --from=build /usr/src/app/dist dist # Copy Output files to appropriate directory from build stage COPY --from=build /usr/src/app/packages packages +LABEL service=activepieces + # Copy frontend files to Nginx document root directory from build stage COPY --from=build /usr/src/app/dist/packages/ui/core/ /usr/share/nginx/html/ VOLUME ${AP_CACHE_PATH} VOLUME ${AP_PACKAGE_ARCHIVE_PATH} - # Set up entrypoint script COPY docker-entrypoint.sh . RUN chmod +x docker-entrypoint.sh diff --git a/docker-compose.yml b/docker-compose.yml index 58d547c7c..af93ee0ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.0' services: activepieces: - image: activepieces/activepieces:0.14.3 + image: activepieces/activepieces:0.16.0 container_name: activepieces restart: unless-stopped ## Enable the following line if you already use AP_EXECUTION_MODE with SANDBOXED or old activepieces, checking the breaking change documentation for more info. diff --git a/docs/_snippets/enterprise-feature.mdx b/docs/_snippets/enterprise-feature.mdx new file mode 100644 index 000000000..cbb10e558 --- /dev/null +++ b/docs/_snippets/enterprise-feature.mdx @@ -0,0 +1,3 @@ + +These features is only available in Paid edition, if you want to use it, please contact us at `sales@activepieces.com`. + \ No newline at end of file diff --git a/docs/about/changelog.mdx b/docs/about/changelog.mdx index 4fc17e919..6ee846f9b 100755 --- a/docs/about/changelog.mdx +++ b/docs/about/changelog.mdx @@ -14,6 +14,178 @@ Semantic versioning is a standardized way of versioning software projects. It us Here's the revised text with improved grammar and spelling: +### 4/1/2023 + +**โœจ Exciting New Features** + +* feat: add hungarian language (#3557) @abuaboud +* feat: support version history (#3281) @abuaboud +* feat: rerun failed flows from runs page (#3505) @MoShizzle +* feat: add users table in platform dashboard (#3508) @abuaboud + +**๐Ÿงฉ Pieces** + +* feat(hubspot): fix contact owner dropdown bug / get owner by email action (#3551) @kishanprmr +* feat(vbout): create contact,tag,email list,email campaign,social media post, remove tag, update contact, unsubscribe contact actions (#3526) @kishanprmr +* fix(ntfy): support unicode api header (#3530) @la3rence +* feat(text-helper): added option to replace only first match for (replace action) (#3524) @AbdulTheActivePiecer +* feat(google-docs): create document using template (#3514) @pfernandez98 +* feat(airtable): fix find record bug / updated record trigger (#3512) @kishanprmr +* feat(google-drive): search folder by name (#3511) @pfernandez98 +* feat(beamer): add changelog and notification center tool (#3468) @i-nithin + +**๐Ÿž Bug Fixes** + +* fix: global api key should be optional (#3555) @abuaboud +* fix: show correct flow status after first publish (on) and refresh runs table after a retry (#3546) @AbdulTheActivePiecer +* fix: AppSumo authn (#3542) @khaledmashaly +* fix: automatically refresh runs table when there is a run still being executed (#3538) @AbdulTheActivePiecer +* fix: skip completed step after resume (#3523) @abuaboud +* fix: verified user migration in sqlite3 (#3521) @abuaboud +* fix: add raw link to emails incase button didn't work (#3507) @khaledmashaly + +**๐ŸŽจ Enhancements & Polish** +* chore(release): skip building arm/v7 and arm/v8 images for RCs (#3541) @khaledmashaly +* fix: build docker image for raspberry pi arm v7/v8 (#3536) @abuaboud + +**๐Ÿ“š Documentation** + +* docs: add la3rence as a contributor for plugin (#3532) @allcontributors + +**๐Ÿงน Maintenance** + +* chore: upgrade typeorm from 0.3.17 to 0.3.18 (#3549) @abuaboud +* chore: enforce license limits (#3525) @abuaboud +* refactor: authorization middleware (#3490) @khaledmashaly +* test: simple flow test for worker (production builds) (#3520) @abuaboud +* chore(deps): bump msgpackr from 1.10.0 to 1.10.1 (#3509) @dependabot + +**Thanks โค๏ธ** +@AbdulTheActivePiecer, @MoShizzle, @abuaboud, @allcontributors, @allcontributors[bot], @dependabot, @dependabot[bot], @eltociear, @i-nithin, @khaledmashaly, @kishanprmr, @la3rence, @pfernandez98 and @snyk-bot + + +### 28/12/2023 + +**โœจ Exciting New Features** + +* feat: manage templates from platform (#3482) @abuaboud +* feat: add new languages Indonesian, Vietnamese, Chinese, and Portuguese (#3451) @abuaboud +* feat: support app connections api (#3389) @abuaboud +* feat: ability to test loop step to fill sample data (#3441) @AbdulTheActivePiecer +* feat: listen to webhook sync response realtime instead of polling (#3431) @abuaboud +* feat(languages): Increased coverage - Japanese 100%, Dutch 100%, Simplified Chinese 18% (#3394) @abuaboud +* feat: viewable link for files when testing steps (#3396) @MoShizzle +* feat: allow platform admins to choose between using our oauth2 apps or not (#3401) @AbdulTheActivePiecer +* feat(i18n): increase coverage in Japanese 57% / Spanish 100% / dutch languages 100% (#3311) @abuaboud +* feat(api): project / project members api (#3383) @abuaboud +* feat(builder): complete revamp of branching and loop rendering (#3216) @abuaboud +* feat: add api keys (#3345) @abuaboud + +**๐Ÿงฉ Pieces** + +* feat(telegram-bot): Fixes for critical issues / Create Invite Link Action / Get Chat Member Action (#3492) @alerdenisov +* fix(twitter): improve error messages shown when a tweet fails (#3497) @AbdulTheActivePiecer +* fix(airtable): "Create record" action used to fail when there was number inputs, now it doesn't (#3488) @AbdulTheActivePiecer +* feat(line): send message action / new message trigger (#3478) @abuaboud +* feat(twitter): added two extra inputs to upload media and improved instructions for authentication (#3483) @AbdulTheActivePiecer +* feat(telegram-bot): send media action and `reply_markup` field in messages (#3485) @alerdenisov +* fix(monday): add missing oauth2 scopes (#3484) @AbdulTheActivePiecer +* feat(discourse): add users to group / change trust level / create post / create topic / send private message actions (#3456) @pfernandez98 +* fix(text-helper): replace all instances of found text, and make (replace) input optional (#3469) @AbdulTheActivePiecer +* feat(stripe): retrieve customer action (#3459) @Doskyft +* chore: update piece names to match brands (#3460) @MoShizzle +* feat(kizeo-forms): adds output format property to triggers (#3452) @BastienMe +* feat(slack): added attachment field to send message action (#3447) @MoShizzle +* feat(google-gemini): add chat action (#3444) @pfernandez98 +* feat(imap): add all mailboxes option for gmail users (#3442) @MoShizzle +* feat(jira-cloud): create issue and search issues actions / new issue and updated issue triggers (#3440) @MoShizzle +* feat(google-gemini): generate content from image / generate content action (#3437) @pfernandez98 +* feat(datehelper): get next day of week and get next day of year actions (#3315) @joeworkman +* feat(bettermode): assign badge, revoke badge, create discussion, create question actions (#3314) @joeworkman +* feat(retable): create record, project, workspace triggers, get all workspaces, get all projects, get all retables actions (#3343) @kishanprmr +* feat(cartloom): create discount, get discount, discounts, order, orders date email, orders date, products actions (#3347) @joeworkman +* feat(moxie-crm): 22 triggers, create a client, create a project, create a task actions (#3359) @kishanprmr +* feat(matomo): add annotation action (#3364) @joeworkman +* feat(bonjoro): create a greet action (#3366) @joeworkman +* feat(clearout): instant verify action (#3367) @joeworkman +* feat(sendy): subscribe multiple lists, unsubscribe multiple lists actions (#3368) @joeworkman +* feat(totalcms): 13 actions, and new blog post trigger (#3378) @joeworkman +* feat(Mastodon): add media file property to post status action (#3397) @denieler +* fix(openai): support fine-tuned model by approximate tokens length (#3392) @MoShizzle +* fix: dynamic fields not working in certopus piece (#3349) @VrajGohil +* feat(wordpress): get post details action (#3330) @pfernandez98 + +**๐Ÿ› ๏ธ Piece Framework** + +* feat: add project info to context (#3453) @MoShizzle +* feat: add client credentials oauth2 (#3329) @abuaboud + +**๐Ÿž Bug Fixes** + +* fix: show url when testing step instead of local path (#3486) @abuaboud +* fix: install piece in platform level is fixed (#3454) @abuaboud +* fix: sync webhook works with redis pubsub (#3439) @abuaboud +* fix: remove package.json lint from new piece project.json (#3430) @MoShizzle +* fix: some dropdowns don't show the selected value for step inputs (#3424) @AbdulTheActivePiecer +* fix: corrects aspect ratio for home button to be 1:1 (#3425) @AbdulTheActivePiecer +* fix: show delete flow within builder in flow actions list and hide the list when showing runs (#3421) @AbdulTheActivePiecer +* fix: trigger not being trimmed when payload too large causing internal error (#3411) @MoShizzle +* fix: better explanation for the Activepieces' OAuth2 apps toggle in platform pieces table (#3409) @AbdulTheActivePiecer +* fix: correct date for chatbots deprecation (#3408) @AbdulTheActivePiecer +* fix(openai): support fine-tuned model by approximate tokens length (#3392) @MoShizzle +* fix: send referring user id with sign up request (#3381) @AbdulTheActivePiecer +* fix: calculation task limit (#3380) @abuaboud +* fix: daily limit tasks query caused database to spike (#3375) @abuaboud +* fix: never send newsletter to platform users (#3369) @abuaboud +* fix: sync webhooks return error status now (#3370) @abuaboud +* fix: webhook /sync response on errors (#3350) @MoShizzle +* fix: project member permission (#3362) @abuaboud +* fix: migrate to oldest ee platform (#3360) @abuaboud +* fix: platform dashboard wasn't accessible for users on first sign in for EE (#3352) @AbdulTheActivePiecer + +**๐ŸŽจ Enhancements & Polish** + +* feat: hide sign up link for platform, sign up are invite only (#3458) @abuaboud +* feat: new design for fullscreen code editor (#3446) @AbdulTheActivePiecer +* feat: improve the loading time of dropdowns with 1000+ options and prevent browser crashing (#3395) @AbdulTheActivePiecer +* feat: introduce flag to hide/show back button and folder name in builder for embedding (#3361) @AbdulTheActivePiecer +* feat: project membership api tweaks (#3344) @khaledmashaly + +**๐Ÿ“š Documentation** + +* docs: add i-nithin as a contributor for plugin (#3470) @allcontributors +* docs: add bigfluffycookie as a contributor for bug (#3438) @allcontributors +* docs: correct a word (#3417) @MrMYHuang +* docs: add MrMYHuang as a contributor for doc (#3418) @allcontributors +* docs: add denieler as a contributor for plugin (#3398) @allcontributors +* docs(endpoints): delete / create project member (#3358) @abuaboud +* docs: projects / flows / connections docs (#3346) @abuaboud +* docs: update codespace instructions (#3323) @pfernandez98 +* docs: add pfernandez98 as a contributor for plugin (#3328) @allcontributors + +**๐Ÿงน Maintenance** + +* chore: rename app name to piece name in connections (#3481) @abuaboud +* chore: convert cloud to platform (#3493) @abuaboud +* chore: deprecate chatbots (#3480) @abuaboud +* refactor: remove `FlowInstance` entity (#3377) @khaledmashaly +* refactor: engine with test coverage (#3111) @abuaboud +* fix: remove package.json lint from new piece project.json (#3430) @MoShizzle +* refactor: move app webhooks secrets to environent variable (#3403) @abuaboud +* refactor: platform owner should check jwt role (#3399) @abuaboud +* refactor: authorization cleanup for unauthenticated endpoints (#3390) @abuaboud +* [Snyk] Security upgrade @sendgrid/mail from 7.7.0 to 8.0.0 (#3355) @abuaboud +* chore: upgrade node from 18.18.1-bullseye to 18.18.2-bullseye (#3331) @abuaboud + +**๐Ÿ–ฅ๏ธ API Changes** + +* chore: rename app name to piece name in connections (#3481) @abuaboud +* feat: initial users management api (#3443) @khaledmashaly +* feat: support app connections api (#3389) @abuaboud + +**Thanks โค๏ธ** +@AbdulTheActivePiecer, @BastienMe, @Doskyft, @MoShizzle, @MrMYHuang, @VrajGohil, @abuaboud, @alerdenisov, @allcontributors, @allcontributors[bot], @crowdin-bot, @denieler, @joeworkman, @khaledmashaly, @kishanprmr, @pfernandez98 and @snyk-bot + ### 1/12/2023 diff --git a/docs/developers/architecture/code-worker.mdx b/docs/developers/architecture/code-worker.mdx deleted file mode 100755 index 75df33b83..000000000 --- a/docs/developers/architecture/code-worker.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: "Code Worker" -description: "" ---- - -**Functional Requirements:** -* Execute NodeJS code from code editor for test purposes. -* Download npm packages and bundle them into single JS file. - -**Terminology:** -* [Webpack](https://webpack.js.org/): tool to bundle javascript projects into single file. -* [Isolate](https://github.com/ioi/isolate): creates a lightweight sandboxes locally. - -## Detailed Design - -### Code Builder - -All codes are written by user are unbundled (package.json and index.js), -after installing the dependencies `node_modules` size grows so fast while the code only use small parts of it. - -The goal to use webpack to bundle only used code into single javascript file, to do that we do following. - -1. Call `isolate` to create sandbox folder locally. -2. Copy the code with webpack configs inside the sandbox. -3. Call `npm install` securely with `isolate`. -4. Call `npm run build` and fetch the bundled js. - -### Code Runner - -When users want to test NodeJS code written on activepieces code editor, the code runner do the following. - -1. Call `isolate` to create sandbox folder locally. -2. Copy the code inside the sandbox. -3. Call `node CODE_FILE.js` securely using isolate and fetch the results. \ No newline at end of file diff --git a/docs/developers/architecture/flow-worker.mdx b/docs/developers/architecture/flow-worker.mdx deleted file mode 100755 index b54f3600f..000000000 --- a/docs/developers/architecture/flow-worker.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: "Flow Worker" -description: "" ---- - -**Functional Requirements:** -* Create Flow Queue to add flow execution requests from multiple sources. -* Consume flow requests and executes flow using `engine`. - -**Non-Functional Requirements:** -* Securely execute untrusted code actions written by the user. -* Queue tasks should be durable, doesn't get lost after shutting down the server. -* The queue should be distributed to allow scaling worker horizontally. - - -**Terminology:** -* Flow Queue: contains flow execution requests from multiple sources. -* Flow Worker: consume flow queue and obtaining the flow results -* [BullMQ](https://github.com/taskforcesh/bullmq): distributed queue system based on `redis` as persistence layer. -* [Isolate](https://github.com/ioi/isolate): creates a lightweight sandboxes locally. - - -## Detailed Design - -### Flow Queue - -The flow queue is based on `BullMQ`, that would make it durable since it stores the tasks on `redis` -and the library also support scheduled tasks. - -The scheduled tasks are required for flows with schedule trigger. - -### Flow Worker - -Consumes the flow queue and do the following in this order: - -**1. Create Sandbox:** - -First we need to decide which sandbox number to use locally, We have a singleton predefined queue contains all numbers from 1 to 20 (randomly picked), -We will pick the first element and remove it from queue and return it once we are done. - -Next we will call isolate binary file to clean and initialize sandbox directory with picked number from the queue. - -After that Isolate should creates an folder in the following path: `/var/local/lib/isolate/BOX_ID`. - -**2. Download Required Files to Sandbox:** - -Now we need to copy all required files to execute flow inside `/var/local/lib/isolate/BOX_ID/box`. - -**Required files:** -* Flow Structure as json -* Codes: Codes written by the user -* Engine: static bundled JS file from `engine` package. - -In the following structure. -``` -. -โ”œโ”€โ”€ BOX_ID -โ”‚ โ”œโ”€โ”€ box -โ”‚ โ”‚ โ”œโ”€โ”€ activepieces-engine.js -โ”‚ โ”‚ โ”œโ”€โ”€ flows -โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ flow_version_id.json -โ”‚ โ”‚ โ”œโ”€โ”€ code -โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ code_one_file_id.js -โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€ code_two_file_id.js - -``` - -**3. Install Pieces into Sandbox:** - -The worker will create package.json and add all required pieces as dependencies, then it will run `pnpm install` inside the sandbox. - -**4. Call The Engine:** - -The worker will ask isolate to sandbox specified command line inside the folder with predefined limitation. - -The command will look something similar to this. - -`./isolate --box-id=%d --processes --wall-time=500 --run node /var/local/lib/isolate/BOX_ID/box/activepieces-engine.js` - - -**5. Parse the Result file:** - -The engine will produce an output file `_output.json` inside the sandbox, the worker will parse this and upload the execution result. diff --git a/docs/developers/building-pieces/create-action.mdx b/docs/developers/building-pieces/create-action.mdx index 961e3adcb..6e86ca28e 100755 --- a/docs/developers/building-pieces/create-action.mdx +++ b/docs/developers/building-pieces/create-action.mdx @@ -64,7 +64,7 @@ You can ask ChatGPT3 to write the function to get top stories from hackernews. ```typescript -import { createAction, Property } from "@activepieces/pieces-framework"; +import { createAction, Property, PieceAuth } from "@activepieces/pieces-framework"; import { httpClient, HttpMethod } from "@activepieces/pieces-common"; export const fetchTopStories = createAction({ @@ -88,7 +88,7 @@ export const fetchTopStories = createAction({ }); const topStoryIds: string[] = topStoryIdsResponse.body; const topStories = []; - for (let i = 0; i < Math.min(context.propsValue['number_of_stories']!, topStoryIds.length); i++) { + for (let i = 0; i < Math.min(context.propsValue['number_of_stories'], topStoryIds.length); i++) { const storyId = topStoryIds[i]; const storyResponse = await httpClient.sendRequest({ method: HttpMethod.GET, diff --git a/docs/developers/building-pieces/triggers/create-polling-trigger.mdx b/docs/developers/building-pieces/triggers/create-polling-trigger.mdx index 73051ece5..18a6d1718 100644 --- a/docs/developers/building-pieces/triggers/create-polling-trigger.mdx +++ b/docs/developers/building-pieces/triggers/create-polling-trigger.mdx @@ -13,7 +13,7 @@ This method runs every **5 minutes**, fetches the endpoint between a certain tim **Testing:** You cannot test it with Test Flow, as it uses static sample data provided in the piece. -To test the trigger, publish the collection, perform the event, and wait for the run to be invoked (every 5 minutes) on the third party. Then check the flow runs from the main dashboard. +To test the trigger, publish the flow, perform the event, and wait for the run to be invoked (every 5 minutes) on the third party. Then check the flow runs from the main dashboard. **Examples:** - [New Record Airtable](https://github.com/activepieces/activepieces/blob/main/packages/pieces/airtable/src/lib/trigger/new-record.trigger.ts) diff --git a/docs/developers/building-pieces/triggers/create-webhook-trigger.mdx b/docs/developers/building-pieces/triggers/create-webhook-trigger.mdx index 3894126f3..c58b95f89 100644 --- a/docs/developers/building-pieces/triggers/create-webhook-trigger.mdx +++ b/docs/developers/building-pieces/triggers/create-webhook-trigger.mdx @@ -19,7 +19,7 @@ Using the `context.store`, fetch the webhook ID from the enable step and delete **Testing:** You cannot test it with Test Flow, as it uses static sample data provided in the piece. -To test the trigger, publish the collection, perform the event. Then check the flow runs from the main dashboard. +To test the trigger, publish the flow, perform the event. Then check the flow runs from the main dashboard. **Examples:** diff --git a/docs/developers/building-pieces/triggers/overview.mdx b/docs/developers/building-pieces/triggers/overview.mdx index a4ef0c7a9..a411d2960 100755 --- a/docs/developers/building-pieces/triggers/overview.mdx +++ b/docs/developers/building-pieces/triggers/overview.mdx @@ -20,11 +20,11 @@ export const createNewIssue = createTrigger({ triggerType: POLLING | WEBHOOK, props: {}; // Required properties from the user. - // Run when the user enable or publish the collection. + // Run when the user enable or publish the flow. onEnable: (ctx) => {}; - // Run when the user disable the collection or - // the old collection is deleted after new one is published. + // Run when the user disable the flow or + // the old flow is deleted after new one is published. onDisable: (ctx) => {}; // Trigger implementation, It takes context as parameter. diff --git a/docs/developers/setup-fork.mdx b/docs/developers/setup-fork.mdx index f46d9773d..86fe1d96a 100644 --- a/docs/developers/setup-fork.mdx +++ b/docs/developers/setup-fork.mdx @@ -6,17 +6,17 @@ icon: "circle-1" The first step to modify a repository is to fork it. Forking a repository creates a copy of the repository on your account. -**Friendly Tip #1:** For experimentation, fork or clone the public repository. For enterprise production, fork the private one. +**Friendly Tip #1:** For experimentation, fork or clone the public repository. -## Create a Public Fork (Community Users) +## Create a Public Fork (Public Pieces) Click the `Fork` button in the top right corner of the repository page (https://github.com/activepieces/activepieces) -## Create a Private Fork (Enterprise Users) +## Create a Private Fork (Private Pieces) -Private forks are valuable for enterprise users seeking to establish private repositories for their organization's use. +Private piece installation is available under the paid edition. You can still develop pieces, contribute them back, **OR** publish them to the public npm registry and use it in your own instance / project. By following these steps, you can create a private fork on GitHub, GitLab or another platform and configure the "activepieces" repository as the upstream source, allowing you to incorporate changes from the "activepieces" repository. diff --git a/docs/enterprise/connection-cards/frontend-sdk.mdx b/docs/embedding/connection-cards/frontend-sdk.mdx similarity index 100% rename from docs/enterprise/connection-cards/frontend-sdk.mdx rename to docs/embedding/connection-cards/frontend-sdk.mdx diff --git a/docs/enterprise/connection-cards/overview.mdx b/docs/embedding/connection-cards/overview.mdx similarity index 87% rename from docs/enterprise/connection-cards/overview.mdx rename to docs/embedding/connection-cards/overview.mdx index 2a392c949..f27230229 100644 --- a/docs/enterprise/connection-cards/overview.mdx +++ b/docs/embedding/connection-cards/overview.mdx @@ -23,4 +23,3 @@ Here's a simple breakdown of how this feature works: 3. You can dynamically utilize this information within your workflow. 4. We've also developed additional tools and pieces that allow you to run the same flow for multiple users. -Integrating this feature into your platform is straightforward, and it opens up new possibilities for seamless automation on behalf of your users using Activepieces. \ No newline at end of file diff --git a/docs/enterprise/connection-cards/user-authentication.mdx b/docs/embedding/connection-cards/user-authentication.mdx similarity index 100% rename from docs/enterprise/connection-cards/user-authentication.mdx rename to docs/embedding/connection-cards/user-authentication.mdx diff --git a/docs/enterprise/connection-cards/user-flows.mdx b/docs/embedding/connection-cards/user-flows.mdx similarity index 100% rename from docs/enterprise/connection-cards/user-flows.mdx rename to docs/embedding/connection-cards/user-flows.mdx diff --git a/docs/platform/embedding.mdx b/docs/embedding/embed-builder/embed-iframe.mdx similarity index 93% rename from docs/platform/embedding.mdx rename to docs/embedding/embed-builder/embed-iframe.mdx index 807874780..3c8a219ff 100644 --- a/docs/platform/embedding.mdx +++ b/docs/embedding/embed-builder/embed-iframe.mdx @@ -1,7 +1,6 @@ --- -title: "Embedding" +title: "Embed Iframe" description: "" -icon: "code" --- To embed Activepieces into iframe, follow these steps: @@ -37,7 +36,7 @@ activepieces.configure({ ## Step 2: Generate a JWT token -Follow the instructions in the [managed users](https://www.activepieces.com/docs/platform/managed-users) page +Follow the instructions in the [generate jwt](./generate-jwt) page ## Step 3: Add an iframe diff --git a/docs/platform/managed-users.mdx b/docs/embedding/embed-builder/generate-jwt.mdx similarity index 89% rename from docs/platform/managed-users.mdx rename to docs/embedding/embed-builder/generate-jwt.mdx index 59d3276fa..1ed6c28ca 100644 --- a/docs/platform/managed-users.mdx +++ b/docs/embedding/embed-builder/generate-jwt.mdx @@ -1,12 +1,9 @@ --- -title: "Managed Users" -description: "" -icon: "user" +title: "Generate JWT Token" +description: "if you own SaaS, you can authenticate your users from your app to the dashboard without asking them to login" --- - -if you own SaaS, you can authenticate your users from your app to the dashboard without asking them to login, this feature is paid and you can contact sales@activepieces.com to have access to it. - + ## Step 1: Obtain Signing Key diff --git a/docs/endpoints/flows/create.mdx b/docs/endpoints/flows/create.mdx new file mode 100644 index 000000000..1eaf3f917 --- /dev/null +++ b/docs/endpoints/flows/create.mdx @@ -0,0 +1,4 @@ +--- +title: 'Create Flow' +openapi: POST /v1/flows/ +--- \ No newline at end of file diff --git a/docs/endpoints/flows/delete.mdx b/docs/endpoints/flows/delete.mdx index 92603420a..760dee049 100644 --- a/docs/endpoints/flows/delete.mdx +++ b/docs/endpoints/flows/delete.mdx @@ -1,4 +1,4 @@ --- title: 'Delete Flow' -openapi: DELETE /v1/flows/{flowId} +openapi: DELETE /v1/flows/{id} --- \ No newline at end of file diff --git a/docs/endpoints/flows/get.mdx b/docs/endpoints/flows/get.mdx index b2f8a17a4..08da51143 100644 --- a/docs/endpoints/flows/get.mdx +++ b/docs/endpoints/flows/get.mdx @@ -1,4 +1,4 @@ --- title: 'Get Flow' -openapi: GET /v1/flows/{flowId} +openapi: GET /v1/flows/{id} --- \ No newline at end of file diff --git a/docs/enterprise/environments.mdx b/docs/enterprise/environments.mdx deleted file mode 100644 index bf08a140c..000000000 --- a/docs/enterprise/environments.mdx +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: "Environments & Git Sync" -description: "" -icon: "git-alt" ---- - - -This feature is not yet complete and will be released under the enterprise edition. Please contact sales@activepieces.com if this is required for you. ๐Ÿšง - - - -This feature is currently being developed closely with existing customers. We would love to hear your thoughts and usecase. - -Feel free to book a meeting with the CTO for technical discussions from https://cal.com/abuaboud. - - -The objective of using environments: - -- **Version Control & External Storage:** Maintain complete version history in Git, in addition to the one in Activepieces that showcases all deployments. -- **CI / CD:** Automate deployment to Activepieces by leveraging your Git-based provider (Github Actions / GitLab). -- **Compliance and Auditability:** manage access to build, test, and deploy automations with complete audit trail. -- **Low-Code User Friendly:** Utilize a standard Git-based repository with direct visual interaction from activepieces interface. - -## How It Works: - -This feature utilizes Git for external storage, version control, and CI/CD capabilities. Before starting, the following requirements are necessary. - -**Requirements:** - -- Production and Development branches and path inside the Git-based repository. -- Two Projects in Activepieces: one for Development and one for Production. - -The development workflow will resemble the following: - -**Step One: Make Changes in Development Project** - -The development team will implement changes within the development project. Once completed, they'll select the flows and visually push them to the Git development branch from Activepieces interface, requiring no Git knowledge. - - -If conflicts occur, Activepieces will be accepted changes, overriding changes in Git. - - -The following will be pushed in JSON format: -- Credentials (Name / Type) only. -- Workflows. - -**Step Two: Open Pull Request (Optional)** - -In Github/GitLab or your Git-based provider, you can open a pull request from the development branch toward the production branch. Once it receives the necessary approval, proceed to merge it. - - -If no approval or reviews are required, you can push directly to the production branch from activepieces to trigger CI/CD. - - -**Step Three: Automate Deployment to Production Project** - -The production project in **Activepieces** will automate pulling from the production branch in **Git**. It will synchronize the exact state from the **Git**-based branch to your **Activepieces** project. - -The CI/CD expects all credentials to exist with the same name and type; otherwise, the pull will fail, providing a proper error indicating missing connections. - - -If there's a conflict, Git will be the source of truth. - - - diff --git a/docs/enterprise/whitelabel.mdx b/docs/enterprise/whitelabel.mdx deleted file mode 100644 index 07fb5147f..000000000 --- a/docs/enterprise/whitelabel.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: "White Labeling" -description: "" -icon: "user" ---- - - -This feature is exclusively available in our paid version. If you are interested in learning more about it or if you have any questions, please don't hesitate to contact us. We are always delighted to assist you. [Schedule a demo](https://calendly.com/activepieces/demo). - - -Our white labeling feature is designed for SaaS companies and agencies seeking to integrate Activepieces seamlessly into their offerings while maintaining a consistent brand identity. - -## Key Features - -When you opt for our white labeling option, you gain access to all the features available in the **cloud edition**, along with the following exciting additions: - -1. **Custom Branding:** Tailor the appearance of the software to align with your brand's identity by selecting your own branding colors and fonts. - -2. **Piece Management:** Take full control over Activepieces pieces. You can show or hide existing pieces and create your own unique pieces to customize the platform according to your specific needs. - -3. **User Authentication Management:** Seamlessly manage user access and permissions by integrating your existing users and teams, granting them the appropriate level of access to Activepieces. - -4. **Template Management:** Control prebuilt templates and add your own unique templates to meet the requirements of your users. - -5. **Single Sign-On (SSO) Integration:** Easily integrate your existing authentication system; - -6. **Enforce (SSO) Integration:** Enforce your users to use your existing authentication system - -7. **Just In Time (JIT) Provisioning:** Automatically provision users in Activepieces when they log in to your existing authentication system for the first time. - -8. **Private Pieces:** Create private pieces exclusive to your organization without sharing them with the community or public npmjs. However, publishing pieces to public npmjs is available for all users in the cloud and open-source. - -9. **Audit Log:** Gain the ability to audit all actions taken by users within the platform for enhanced security and compliance. diff --git a/docs/flows/code.mdx b/docs/flows/code.mdx deleted file mode 100644 index 95b8419a8..000000000 --- a/docs/flows/code.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: "Code Piece" -description: "" -icon: "code" ---- - -## Overview - -The Code piece is where you can write your custom code. It maximizes flexibility as you can write code to achieve things you can't with the predefined pieces. - -This piece currently supports writing code in **Typescript**, you can also bring in your favorite **npm packages** by clicking on the fullscreen button and **Add npm package***. - -This is how the settings of this piece will look when you add it to your flow: - - - -## Use cases - -You can do almost anything you do with regular code. For example: - -- Use a package to process a CSV file and parse it as a JSON array. -- Reformat output from previous steps to a different format expected by the following pieces. -- Filter outputs from previous steps by a custom criteria. - -## Instructions - -As you see in the screenshot of the step settings above, you can configure the Code piece as follows: - -### Parameters - -To use outputs from previous steps in your code, you have to pass them through the Parameters input. - -Parameters are key/value pairs. The **key** you define here will become available in your code as a property of the `inputs` argument in the main function. - -For example, if you pass `mode` as a **key** and `full` as a **value**, you can access this parameter by `inputs.mode` and its value will be `full`. - -The parameters become powerful when you pass outputs from previous steps. - -## Fullscreen - -You can go to fullscreen to add npm packages and have a better coding and testing experience by clicking the fullscreen button. - -![title](/resources/screenshots/code-fullscreen.png) \ No newline at end of file diff --git a/docs/install/configurations/environment-variables.mdx b/docs/install/configurations/environment-variables.mdx index a7c964799..c9780b220 100644 --- a/docs/install/configurations/environment-variables.mdx +++ b/docs/install/configurations/environment-variables.mdx @@ -39,6 +39,7 @@ To configure activepieces, you will need to set some environment variables, Ther | `AP_REDIS_PASSWORD` | โ—๏ธ Password to use when connect to redis | `None` | | | `AP_REDIS_HOST` | โ—๏ธ The hostname or IP address of the Redis server | `None` | | | `AP_REDIS_PORT` | โ—๏ธ The port number for the Redis server | `None` | | +| `AP_RESIS_DB` | The Redis database index to use | `0` | | | `AP_REDIS_USE_SSL` | Connect to Redis with SSL | `false` | | | `AP_NOTIFICATION_URL` | The notification URL is triggered whenever a run fails. | `false` | [https://cloud.activepieces.com/flows/WEBHOOK\_TRIGGER\_FLOW\_ID](https://cloud.activepieces.com/flows/WEBHOOK_TRIGGER_FLOW_ID) | | `AP_SIGN_UP_ENABLED` | Enable the Sign up, It requires sandboxing to be on | `false` | | diff --git a/docs/install/options/overview.mdx b/docs/install/options/overview.mdx index 8d5a4c075..bdd9990d4 100755 --- a/docs/install/options/overview.mdx +++ b/docs/install/options/overview.mdx @@ -3,10 +3,10 @@ title: "Overview" description: "Introduction to the different ways to install Activepieces" --- -Activepieces Community Edition can be deployed using **Docker**, **Docker Compose** and **Kubernetes**. +Activepieces Community Edition can be deployed using **Docker**, **Docker Compose**, and **Kubernetes**. -Community Editions is focused on **individuals**, It is **free** and **open source**. +Community Edition is **free** and **open source**. You can read the difference between the editions [here](.././editions). @@ -15,11 +15,11 @@ You can read the difference between the editions [here](.././editions). -Deploy Activepieces as a single Docker container using the sqllite database. +Deploy Activepieces as a single Docker container using the SQLite database. - Deploy Activepieces with **redis** and **postgres** setup + Deploy Activepieces with **Redis** and **PostgreSQL** setup. @@ -48,20 +48,28 @@ Deploy Activepieces as a single Docker container using the sqllite database. - Install on AWS with Pulumi + Install on AWS with Pulumi. - Install on GCP as VM template + Install on GCP as a VM template. + + + + } href="https://repocloud.io/details/?app_id=177"> + Install on RepoCloud. + + ## Cloud Edition - This is the fastest + This is the fastest. - \ No newline at end of file + diff --git a/docs/mint.json b/docs/mint.json index 9a9a0d5bd..af97797c2 100755 --- a/docs/mint.json +++ b/docs/mint.json @@ -110,6 +110,17 @@ "endpoints/connections/list", "endpoints/connections/delete" ] + }, + { + "group": "Flows", + "icon": "bolt", + "pages": [ + "endpoints/flows/schema", + "endpoints/flows/create", + "endpoints/flows/get", + "endpoints/flows/list", + "endpoints/flows/delete" + ] } ] }, @@ -119,15 +130,7 @@ "flows/building-flows", "flows/passing-data", "flows/publishing-flows", - "flows/debugging-runs", - "flows/code" - ] - }, - { - "group": "Security", - "pages": [ - "security/permissions", - "security/sso" + "flows/debugging-runs" ] }, { @@ -215,11 +218,33 @@ ] }, { - "group": "Worker Architecture", + "group": "Architecture", + "pages": [ + "developers/architecture/repo-structure" + ] + }, + { + "group": "Embedding", "pages": [ - "developers/architecture/repo-structure", - "developers/architecture/flow-worker", - "developers/architecture/code-worker" + { + "group": "Embed Builder", + "icon": "code", + "pages": [ + "embedding/embed-builder/generate-jwt", + "embedding/embed-builder/embed-iframe" + + ] + }, + { + "group": "Connection Cards", + "icon": "cards-blank", + "pages": [ + "embedding/connection-cards/overview", + "embedding/connection-cards/user-authentication", + "embedding/connection-cards/frontend-sdk", + "embedding/connection-cards/user-flows" + ] + } ] }, { @@ -232,26 +257,21 @@ "platform/manage-templates", "platform/manage-pieces", "platform/manage-oauth2", - "platform/customize-emails", - "platform/managed-users", - "platform/embedding" + "platform/customize-emails" ] }, { - "group": "Enterprise Edition", + "group": "Operations", "pages": [ - "enterprise/whitelabel", - "enterprise/environments", - { - "group": "Connection Cards", - "icon": "cards-blank", - "pages": [ - "enterprise/connection-cards/overview", - "enterprise/connection-cards/user-authentication", - "enterprise/connection-cards/frontend-sdk", - "enterprise/connection-cards/user-flows" - ] - } + "operations/git-sync", + "operations/audit-logs" + ] + }, + { + "group": "Security", + "pages": [ + "security/permissions", + "security/sso" ] }, { diff --git a/docs/openapi.json b/docs/openapi.json index c98ffcb35..f05da538c 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -266,392 +266,1273 @@ "type": "string", "nullable": true }, - "version": { + "status": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ENABLED" + ] + }, + { + "type": "string", + "enum": [ + "DISABLED" + ] + } + ] + }, + "schedule": { "type": "object", "properties": { - "id": { - "type": "string" + "type": { + "type": "string", + "enum": [ + "CRON_EXPRESSION" + ] }, - "created": { + "cronExpression": { "type": "string" }, - "updated": { + "timezone": { "type": "string" + } + }, + "required": [ + "type", + "cronExpression", + "timezone" + ], + "nullable": true + }, + "publishedVersionId": { + "type": "string", + "nullable": true + } + }, + "required": [ + "id", + "created", + "updated", + "projectId", + "status" + ] + }, + "app-connection": { + "description": "App connection is a connection to an external app.", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "created": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "anyOf": [ + { + "type": "string", + "enum": [ + "OAUTH2" + ] }, - "flowId": { - "type": "string" + { + "type": "string", + "enum": [ + "PLATFORM_OAUTH2" + ] }, - "displayName": { - "type": "string" + { + "type": "string", + "enum": [ + "CLOUD_OAUTH2" + ] }, - "trigger": { - "anyOf": [ - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "valid": { - "type": "boolean" - }, - "displayName": { - "type": "string" - }, - "nextAction": {}, - "type": { - "type": "string", - "enum": [ - "WEBHOOK" - ] + { + "type": "string", + "enum": [ + "SECRET_TEXT" + ] + }, + { + "type": "string", + "enum": [ + "BASIC_AUTH" + ] + }, + { + "type": "string", + "enum": [ + "CUSTOM_AUTH" + ] + } + ] + }, + "pieceName": { + "type": "string" + }, + "projectId": { + "pattern": "^[0-9a-zA-Z]{21}$", + "type": "string" + }, + "status": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ACTIVE" + ] + }, + { + "type": "string", + "enum": [ + "ERROR" + ] + } + ] + } + }, + "required": [ + "id", + "created", + "updated", + "name", + "type", + "pieceName", + "projectId", + "status" + ] + } + } + }, + "paths": { + "/v1/flows/": { + "post": { + "tags": [ + "flows" + ], + "description": "Create a flow", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "displayName": { + "type": "string" + }, + "folderId": { + "type": "string" + }, + "projectId": { + "type": "string" + } + }, + "required": [ + "displayName", + "projectId" + ] + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "created": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "folderId": { + "type": "string", + "nullable": true + }, + "status": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ENABLED" + ] + }, + { + "type": "string", + "enum": [ + "DISABLED" + ] + } + ] + }, + "schedule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "CRON_EXPRESSION" + ] + }, + "cronExpression": { + "type": "string" + }, + "timezone": { + "type": "string" + } }, - "settings": { - "type": "object", - "properties": { - "inputUiInfo": { - "additionalProperties": true, - "type": "object", - "properties": { - "currentSelectedData": {}, - "customizedInputs": { - "type": "object", - "additionalProperties": {} + "required": [ + "type", + "cronExpression", + "timezone" + ], + "nullable": true + }, + "publishedVersionId": { + "type": "string", + "nullable": true + }, + "version": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "created": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "flowId": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "trigger": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "WEBHOOK" + ] + }, + "settings": { + "type": "object", + "properties": { + "inputUiInfo": { + "additionalProperties": true, + "type": "object", + "properties": { + "currentSelectedData": {}, + "customizedInputs": { + "type": "object", + "additionalProperties": {} + }, + "lastTestDate": { + "type": "string" + } + } + } + }, + "required": [ + "inputUiInfo" + ] + } }, - "lastTestDate": { - "type": "string" - } + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "PIECE_TRIGGER" + ] + }, + "settings": { + "type": "object", + "properties": { + "pieceName": { + "type": "string" + }, + "pieceVersion": { + "type": "string", + "pattern": "^([~^])?[0-9]+\\.[0-9]+\\.[0-9]+$" + }, + "pieceType": { + "anyOf": [ + { + "type": "string", + "enum": [ + "CUSTOM" + ] + }, + { + "type": "string", + "enum": [ + "OFFICIAL" + ] + } + ] + }, + "packageType": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ARCHIVE" + ] + }, + { + "type": "string", + "enum": [ + "REGISTRY" + ] + } + ] + }, + "triggerName": { + "type": "string" + }, + "input": { + "type": "object", + "additionalProperties": {} + }, + "inputUiInfo": { + "additionalProperties": true, + "type": "object", + "properties": { + "currentSelectedData": {}, + "customizedInputs": { + "type": "object", + "additionalProperties": {} + }, + "lastTestDate": { + "type": "string" + } + } + } + }, + "required": [ + "pieceName", + "pieceVersion", + "pieceType", + "packageType", + "triggerName", + "input", + "inputUiInfo" + ] + } + }, + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "EMPTY" + ] + }, + "settings": {} + }, + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] } - } + ] }, - "required": [ - "inputUiInfo" - ] - } - }, - "required": [ - "name", - "valid", - "displayName", - "type", - "settings" - ] - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "valid": { - "type": "boolean" - }, - "displayName": { - "type": "string" - }, - "nextAction": {}, - "type": { - "type": "string", - "enum": [ - "PIECE_TRIGGER" - ] + "updatedBy": { + "type": "string", + "nullable": true + }, + "valid": { + "type": "boolean" + }, + "state": { + "anyOf": [ + { + "type": "string", + "enum": [ + "LOCKED" + ] + }, + { + "type": "string", + "enum": [ + "DRAFT" + ] + } + ] + } }, - "settings": { + "required": [ + "id", + "created", + "updated", + "flowId", + "displayName", + "trigger", + "valid", + "state" + ] + } + }, + "required": [ + "id", + "created", + "updated", + "projectId", + "status", + "version" + ] + } + } + } + } + } + }, + "get": { + "tags": [ + "flows" + ], + "description": "List flows", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "folderId", + "required": false + }, + { + "schema": { + "type": "number" + }, + "in": "query", + "name": "limit", + "required": false + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "cursor", + "required": false + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ENABLED" + ] + }, + { + "type": "string", + "enum": [ + "DISABLED" + ] + } + ] + }, + "in": "query", + "name": "status", + "required": false + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "projectId", + "required": true + } + ], + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "type": "object", "properties": { - "pieceName": { + "id": { + "type": "string" + }, + "created": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "projectId": { "type": "string" }, - "pieceVersion": { + "folderId": { "type": "string", - "pattern": "^([~^])?[0-9]+\\.[0-9]+\\.[0-9]+$" + "nullable": true }, - "pieceType": { + "status": { "anyOf": [ { "type": "string", "enum": [ - "CUSTOM" + "ENABLED" ] }, { "type": "string", "enum": [ - "OFFICIAL" + "DISABLED" ] } ] }, - "packageType": { - "anyOf": [ - { + "schedule": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": [ - "ARCHIVE" + "CRON_EXPRESSION" + ] + }, + "cronExpression": { + "type": "string" + }, + "timezone": { + "type": "string" + } + }, + "required": [ + "type", + "cronExpression", + "timezone" + ], + "nullable": true + }, + "publishedVersionId": { + "type": "string", + "nullable": true + }, + "version": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "created": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "flowId": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "trigger": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "WEBHOOK" + ] + }, + "settings": { + "type": "object", + "properties": { + "inputUiInfo": { + "additionalProperties": true, + "type": "object", + "properties": { + "currentSelectedData": {}, + "customizedInputs": { + "type": "object", + "additionalProperties": {} + }, + "lastTestDate": { + "type": "string" + } + } + } + }, + "required": [ + "inputUiInfo" + ] + } + }, + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "PIECE_TRIGGER" + ] + }, + "settings": { + "type": "object", + "properties": { + "pieceName": { + "type": "string" + }, + "pieceVersion": { + "type": "string", + "pattern": "^([~^])?[0-9]+\\.[0-9]+\\.[0-9]+$" + }, + "pieceType": { + "anyOf": [ + { + "type": "string", + "enum": [ + "CUSTOM" + ] + }, + { + "type": "string", + "enum": [ + "OFFICIAL" + ] + } + ] + }, + "packageType": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ARCHIVE" + ] + }, + { + "type": "string", + "enum": [ + "REGISTRY" + ] + } + ] + }, + "triggerName": { + "type": "string" + }, + "input": { + "type": "object", + "additionalProperties": {} + }, + "inputUiInfo": { + "additionalProperties": true, + "type": "object", + "properties": { + "currentSelectedData": {}, + "customizedInputs": { + "type": "object", + "additionalProperties": {} + }, + "lastTestDate": { + "type": "string" + } + } + } + }, + "required": [ + "pieceName", + "pieceVersion", + "pieceType", + "packageType", + "triggerName", + "input", + "inputUiInfo" + ] + } + }, + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "EMPTY" + ] + }, + "settings": {} + }, + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] + } + ] + }, + "updatedBy": { + "type": "string", + "nullable": true + }, + "valid": { + "type": "boolean" + }, + "state": { + "anyOf": [ + { + "type": "string", + "enum": [ + "LOCKED" + ] + }, + { + "type": "string", + "enum": [ + "DRAFT" + ] + } ] + } + }, + "required": [ + "id", + "created", + "updated", + "flowId", + "displayName", + "trigger", + "valid", + "state" + ] + } + }, + "required": [ + "id", + "created", + "updated", + "projectId", + "status", + "version" + ] + } + }, + "next": { + "description": "Cursor to the next page", + "type": "string", + "nullable": true + }, + "previous": { + "description": "Cursor to the previous page", + "type": "string", + "nullable": true + } + }, + "required": [ + "data" + ] + } + } + } + } + } + } + }, + "/v1/flows/{id}": { + "get": { + "tags": [ + "flows" + ], + "description": "Get a flow by id", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "versionId", + "required": false + }, + { + "schema": { + "pattern": "^[0-9a-zA-Z]{21}$", + "type": "string" + }, + "in": "path", + "name": "id", + "required": true + } + ], + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "created": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "folderId": { + "type": "string", + "nullable": true + }, + "status": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ENABLED" + ] + }, + { + "type": "string", + "enum": [ + "DISABLED" + ] + } + ] + }, + "schedule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "CRON_EXPRESSION" + ] + }, + "cronExpression": { + "type": "string" + }, + "timezone": { + "type": "string" + } + }, + "required": [ + "type", + "cronExpression", + "timezone" + ], + "nullable": true + }, + "publishedVersionId": { + "type": "string", + "nullable": true + }, + "version": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "created": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "flowId": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "trigger": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "WEBHOOK" + ] + }, + "settings": { + "type": "object", + "properties": { + "inputUiInfo": { + "additionalProperties": true, + "type": "object", + "properties": { + "currentSelectedData": {}, + "customizedInputs": { + "type": "object", + "additionalProperties": {} + }, + "lastTestDate": { + "type": "string" + } + } + } + }, + "required": [ + "inputUiInfo" + ] + } }, - { - "type": "string", - "enum": [ - "REGISTRY" - ] - } - ] - }, - "triggerName": { - "type": "string" - }, - "input": { - "type": "object", - "additionalProperties": {} - }, - "inputUiInfo": { - "additionalProperties": true, - "type": "object", - "properties": { - "currentSelectedData": {}, - "customizedInputs": { - "type": "object", - "additionalProperties": {} + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "PIECE_TRIGGER" + ] + }, + "settings": { + "type": "object", + "properties": { + "pieceName": { + "type": "string" + }, + "pieceVersion": { + "type": "string", + "pattern": "^([~^])?[0-9]+\\.[0-9]+\\.[0-9]+$" + }, + "pieceType": { + "anyOf": [ + { + "type": "string", + "enum": [ + "CUSTOM" + ] + }, + { + "type": "string", + "enum": [ + "OFFICIAL" + ] + } + ] + }, + "packageType": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ARCHIVE" + ] + }, + { + "type": "string", + "enum": [ + "REGISTRY" + ] + } + ] + }, + "triggerName": { + "type": "string" + }, + "input": { + "type": "object", + "additionalProperties": {} + }, + "inputUiInfo": { + "additionalProperties": true, + "type": "object", + "properties": { + "currentSelectedData": {}, + "customizedInputs": { + "type": "object", + "additionalProperties": {} + }, + "lastTestDate": { + "type": "string" + } + } + } + }, + "required": [ + "pieceName", + "pieceVersion", + "pieceType", + "packageType", + "triggerName", + "input", + "inputUiInfo" + ] + } }, - "lastTestDate": { - "type": "string" - } + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "valid": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "nextAction": {}, + "type": { + "type": "string", + "enum": [ + "EMPTY" + ] + }, + "settings": {} + }, + "required": [ + "name", + "valid", + "displayName", + "type", + "settings" + ] } - } + ] }, - "required": [ - "pieceName", - "pieceVersion", - "pieceType", - "packageType", - "triggerName", - "input", - "inputUiInfo" - ] - } - }, - "required": [ - "name", - "valid", - "displayName", - "type", - "settings" - ] - }, - { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "valid": { - "type": "boolean" - }, - "displayName": { - "type": "string" - }, - "nextAction": {}, - "type": { - "type": "string", - "enum": [ - "EMPTY" - ] + "updatedBy": { + "type": "string", + "nullable": true + }, + "valid": { + "type": "boolean" + }, + "state": { + "anyOf": [ + { + "type": "string", + "enum": [ + "LOCKED" + ] + }, + { + "type": "string", + "enum": [ + "DRAFT" + ] + } + ] + } }, - "settings": {} - }, - "required": [ - "name", - "valid", - "displayName", - "type", - "settings" - ] - } - ] - }, - "updatedBy": { - "type": "string" - }, - "valid": { - "type": "boolean" - }, - "state": { - "anyOf": [ - { - "type": "string", - "enum": [ - "LOCKED" - ] + "required": [ + "id", + "created", + "updated", + "flowId", + "displayName", + "trigger", + "valid", + "state" + ] + } }, - { - "type": "string", - "enum": [ - "DRAFT" - ] - } - ] - } - }, - "required": [ - "id", - "created", - "updated", - "flowId", - "displayName", - "trigger", - "updatedBy", - "valid", - "state" - ] - }, - "status": { - "anyOf": [ - { - "type": "string", - "enum": [ - "ENABLED" - ] - }, - { - "type": "string", - "enum": [ - "DISABLED" - ] - }, - { - "type": "string", - "enum": [ - "UNPUBLISHED" - ] - } - ] - }, - "schedule": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "CRON_EXPRESSION" - ] - }, - "cronExpression": { - "type": "string" - }, - "timezone": { - "type": "string" + "required": [ + "id", + "created", + "updated", + "projectId", + "status", + "version" + ] + } } - }, - "required": [ - "type", - "cronExpression", - "timezone" - ] + } } - }, - "required": [ - "id", - "created", - "updated", - "projectId", - "version", - "status" - ] + } }, - "app-connection": { - "description": "App connection is a connection to an external app.", - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "created": { - "type": "string" - }, - "updated": { - "type": "string" - }, - "name": { - "type": "string" - }, - "type": { - "anyOf": [ - { - "type": "string", - "enum": [ - "OAUTH2" - ] - }, - { - "type": "string", - "enum": [ - "PLATFORM_OAUTH2" - ] - }, - { - "type": "string", - "enum": [ - "CLOUD_OAUTH2" - ] - }, - { - "type": "string", - "enum": [ - "SECRET_TEXT" - ] - }, - { - "type": "string", - "enum": [ - "BASIC_AUTH" - ] - }, - { - "type": "string", - "enum": [ - "CUSTOM_AUTH" - ] - } - ] - }, - "appName": { - "type": "string" - }, - "projectId": { - "pattern": "^[0-9a-zA-Z]{21}$", - "type": "string" - }, - "status": { - "anyOf": [ - { - "type": "string", - "enum": [ - "ACTIVE" - ] - }, - { - "type": "string", - "enum": [ - "ERROR" - ] - } - ] + "delete": { + "tags": [ + "flows" + ], + "description": "Delete a flow", + "parameters": [ + { + "schema": { + "pattern": "^[0-9a-zA-Z]{21}$", + "type": "string" + }, + "in": "path", + "name": "id", + "required": true } - }, - "required": [ - "id", - "created", - "updated", - "name", - "type", - "appName", - "projectId", - "status" - ] + ], + "responses": { + "204": { + "description": "Default Response" + } + } } - } - }, - "paths": { + }, "/v1/app-connections/": { "post": { "tags": [ @@ -671,7 +1552,7 @@ "name": { "type": "string" }, - "appName": { + "pieceName": { "type": "string" }, "projectId": { @@ -704,7 +1585,7 @@ }, "required": [ "name", - "appName", + "pieceName", "projectId", "type", "value" @@ -718,7 +1599,7 @@ "name": { "type": "string" }, - "appName": { + "pieceName": { "type": "string" }, "projectId": { @@ -810,7 +1691,7 @@ }, "required": [ "name", - "appName", + "pieceName", "projectId", "type", "value" @@ -824,7 +1705,7 @@ "name": { "type": "string" }, - "appName": { + "pieceName": { "type": "string" }, "projectId": { @@ -893,7 +1774,7 @@ }, "required": [ "name", - "appName", + "pieceName", "projectId", "type", "value" @@ -907,7 +1788,7 @@ "name": { "type": "string" }, - "appName": { + "pieceName": { "type": "string" }, "projectId": { @@ -980,7 +1861,7 @@ }, "required": [ "name", - "appName", + "pieceName", "projectId", "type", "value" @@ -994,7 +1875,7 @@ "name": { "type": "string" }, - "appName": { + "pieceName": { "type": "string" }, "projectId": { @@ -1031,7 +1912,7 @@ }, "required": [ "name", - "appName", + "pieceName", "projectId", "type", "value" @@ -1045,7 +1926,7 @@ "name": { "type": "string" }, - "appName": { + "pieceName": { "type": "string" }, "projectId": { @@ -1079,7 +1960,7 @@ }, "required": [ "name", - "appName", + "pieceName", "projectId", "type", "value" @@ -1123,7 +2004,7 @@ "type": "string" }, "in": "query", - "name": "appName", + "name": "pieceName", "required": false }, { @@ -1201,7 +2082,7 @@ } ] }, - "appName": { + "pieceName": { "type": "string" }, "projectId": { @@ -1231,7 +2112,7 @@ "updated", "name", "type", - "appName", + "pieceName", "projectId", "status" ] @@ -1295,6 +2176,9 @@ "properties": { "displayName": { "type": "string" + }, + "externalId": { + "type": "string" } }, "required": [ diff --git a/docs/operations/audit-logs.mdx b/docs/operations/audit-logs.mdx new file mode 100644 index 000000000..b18a6e728 --- /dev/null +++ b/docs/operations/audit-logs.mdx @@ -0,0 +1,11 @@ +--- +title: "Audit Log" +description: "" +icon: "book" +--- + + + + +Documentation for Activepieces Audit logs is coming soon. ๐Ÿšง + \ No newline at end of file diff --git a/docs/operations/git-sync.mdx b/docs/operations/git-sync.mdx new file mode 100644 index 000000000..936ffd324 --- /dev/null +++ b/docs/operations/git-sync.mdx @@ -0,0 +1,35 @@ +--- +title: "Environments & Git Sync" +description: "" +icon: "git-alt" +--- + + + +The Git Sync feature allows for the creation of an **external backup**, **environments**, and maintaining a **version history**. + + +### How It Works: + +This example explains a simple setup for creating development and production environments. The setup can be extended to include multiple environments and multiple Git branches. + +**Requirements:** +- Empty Git Repository +- Two Projects in Activepieces: one for Development and one for Production. + +#### 1. Push to the repository + +After making changes in the development project, you can click on the "Push to Repository" button on the Git Sync page in Activepieces to push the changes to the repository. + +All flows in the Git Repository will be replaced with the ones in the Project. + +#### 2. Pull from the repository + +Please note that the credentials will not be synced automatically. You should manually create the same credentials in both environments. + + +You can trigger a pull from the Git Repository button in Activepeces, then all flows in the project will be replaced with the ones in the git repository. + +#### Approval Workflow (Optional) + +To manage your approval workflow, you can use Git by creating two branches: development and production. Then, you can use standard pull requests as the approval step. diff --git a/docs/platform/appearance.mdx b/docs/platform/appearance.mdx index 385c182b1..940da7c2c 100644 --- a/docs/platform/appearance.mdx +++ b/docs/platform/appearance.mdx @@ -4,6 +4,8 @@ description: "" icon: "palette" --- + + To tweak how your platform looks, head to the **Appearance** section under **Settings**. Here, you can customize: - Logo - Logo Icon diff --git a/docs/platform/custom-domain.mdx b/docs/platform/custom-domain.mdx index 6efb370b4..bb58a3d3c 100644 --- a/docs/platform/custom-domain.mdx +++ b/docs/platform/custom-domain.mdx @@ -4,6 +4,8 @@ description: "" icon: "globe" --- + + You can set up a unique domain for your platform, like app.example.com. This is also used to determine the theme and branding on the authentication pages when a user is not logged in. diff --git a/docs/platform/customize-emails.mdx b/docs/platform/customize-emails.mdx index 6899e52e3..ab5d5e36d 100644 --- a/docs/platform/customize-emails.mdx +++ b/docs/platform/customize-emails.mdx @@ -4,6 +4,8 @@ description: "" icon: "envelope" --- + + You can add your own mail server to Activepieces, or override it if it's in the cloud. From the platform, all email templates are automatically whitelabeled according to the [appearance settings](https://www.activepieces.com/docs/platform/appearance). ![Manage SMTP](/resources/screenshots/manage-smtp.png) diff --git a/docs/platform/manage-oauth2.mdx b/docs/platform/manage-oauth2.mdx index 6c3a82cd9..39b67b8a7 100644 --- a/docs/platform/manage-oauth2.mdx +++ b/docs/platform/manage-oauth2.mdx @@ -4,6 +4,7 @@ description: "" icon: "key" --- + The project automatically uses Activepieces OAuth2 Apps as the default setting. If you prefer to use your own OAuth2 Apps, you can click on the 'Gear Icon' on the piece from the 'Manage Pieces' page and enter your own OAuth2 Apps details. diff --git a/docs/platform/manage-pieces.mdx b/docs/platform/manage-pieces.mdx index f1417fe71..8129f6ed5 100644 --- a/docs/platform/manage-pieces.mdx +++ b/docs/platform/manage-pieces.mdx @@ -4,6 +4,8 @@ description: "" icon: "puzzle-piece" --- + + ## Hide / Show Pieces You can show or hide pieces by clicking on the eye icon next to each piece. diff --git a/docs/platform/manage-projects.mdx b/docs/platform/manage-projects.mdx index 9b07525ae..a5b53953d 100644 --- a/docs/platform/manage-projects.mdx +++ b/docs/platform/manage-projects.mdx @@ -4,6 +4,8 @@ description: "" icon: "building" --- + + You can **create** new projects and sets **limits** on the number of tasks and users for each project. ![Manage Projects](/resources/screenshots/manage-projects.png) diff --git a/docs/platform/manage-templates.mdx b/docs/platform/manage-templates.mdx index 647218c88..fef475233 100644 --- a/docs/platform/manage-templates.mdx +++ b/docs/platform/manage-templates.mdx @@ -4,6 +4,8 @@ description: "" icon: "star" --- + + You can create custom templates for your users within the Platform dashboard's. ![Manage Templates](/resources/screenshots/manage-templates.png) diff --git a/docs/platform/overview.mdx b/docs/platform/overview.mdx index ace4300b6..1279bf896 100644 --- a/docs/platform/overview.mdx +++ b/docs/platform/overview.mdx @@ -4,9 +4,7 @@ description: "" icon: "cube" --- - -This platform with all features in this section is only available for Paid edition, if you want to use it, please contact us at `sales@activepieces.com`. - + The platform is the admin panel for managing your instance. It's suitable for SaaS, Embedd, or agencies that want to white-label Activepieces and offer it to their customers. With this platform, you can: diff --git a/docs/security/permissions.mdx b/docs/security/permissions.mdx index 006d35ea5..a9c38cdd9 100644 --- a/docs/security/permissions.mdx +++ b/docs/security/permissions.mdx @@ -1,5 +1,5 @@ --- -title: "Permissions" +title: "Project Permissions" description: "" icon: 'user' --- diff --git a/package-lock.json b/package-lock.json index aa3333afc..48a3366bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "activepieces", - "version": "0.14.3", + "version": "0.16.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "activepieces", - "version": "0.14.3", + "version": "0.16.0", "dependencies": { "@0xpolygonid/js-sdk": "1.4.2", "@angular/animations": "16.2.6", @@ -91,6 +91,7 @@ "contentful-management": "10.42.0", "cron-validator": "1.3.1", "cronstrue": "2.31.0", + "cross-env": "7.0.3", "crypto-js": "4.2.0", "dayjs": "1.11.9", "decompress": "4.2.1", @@ -107,7 +108,9 @@ "firebase-scrypt": "2.2.0", "font-awesome": "4.7.0", "form-data": "4.0.0", + "fs-extra": "11.2.0", "google-auth-library": "8.9.0", + "googleapis": "129.0.0", "http-status-codes": "2.2.0", "imap": "0.8.19", "import-fresh": "3.3.0", @@ -128,6 +131,7 @@ "marked": "4.3.0", "mime-types": "2.1.35", "monaco-editor": "0.34.1", + "monday-sdk-js": "0.5.2", "mustache": "4.2.0", "nanoid": "3.3.6", "ngx-autosize": "2.0.4", @@ -161,6 +165,7 @@ "rxjs": "7.8.1", "shade-generator": "1.2.7", "showdown": "2.1.0", + "simple-git": "3.21.0", "snarkjs": "0.7.2", "soap": "1.0.0", "sqlite3": "5.1.6", @@ -172,7 +177,7 @@ "tsconfig-paths": "4.2.0", "tslib": "2.6.2", "twitter-api-v2": "1.15.1", - "typeorm": "0.3.17", + "typeorm": "0.3.18", "viem": "1.12.2", "xml2js": "0.6.2", "zone.js": "0.13.1" @@ -7158,7 +7163,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -7175,7 +7179,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -7187,7 +7190,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -7198,14 +7200,12 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -7222,7 +7222,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -7237,7 +7236,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -7725,6 +7723,19 @@ "resolved": "https://registry.npmjs.org/@juanelas/base64/-/base64-1.1.5.tgz", "integrity": "sha512-mjAF27LzwfYobdwqnxZgeucbKT5wRRNvILg3h5OvCWK+3F7mw/A1tnjHnNiTYtLmTvT/bM1jA5AX7eQawDGs1w==" }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" + }, "node_modules/@ledgerhq/connect-kit-loader": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@ledgerhq/connect-kit-loader/-/connect-kit-loader-1.1.8.tgz", @@ -10368,7 +10379,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" @@ -16912,6 +16922,15 @@ "ieee754": "^1.1.13" } }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "b4a": "^1.0.1", + "nanoassert": "^2.0.0" + } + }, "node_modules/blake2b-wasm": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz", @@ -19063,6 +19082,23 @@ "cronstrue": "bin/cli.js" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-fetch": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", @@ -19974,6 +20010,7 @@ "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -20811,8 +20848,7 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ecc-jsbn": { "version": "0.1.2", @@ -22895,9 +22931,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "funding": [ { "type": "individual", @@ -22941,7 +22977,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -22957,7 +22992,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -23213,7 +23247,6 @@ "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -23694,6 +23727,222 @@ "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" }, + "node_modules/googleapis": { + "version": "129.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-129.0.0.tgz", + "integrity": "sha512-gFatrzby+oh/GxEeMhJOKzgs9eG7yksRcTon9b+kPie4ZnDSgGQ85JgtUaBtLSBkcKpUKukdSP6Km1aCjs4y4Q==", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.0.1.tgz", + "integrity": "sha512-mgt5zsd7zj5t5QXvDanjWguMdHAcJmmDrF9RkInCecNsyV7S7YtGqm5v2IWONNID88osb7zmx5FtrAP12JfD0w==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^6.0.3", + "google-auth-library": "^9.0.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/googleapis-common/node_modules/gaxios": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", + "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis-common/node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis-common/node_modules/google-auth-library": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.4.1.tgz", + "integrity": "sha512-Chs7cuzDuav8W/BXOoRgSXw4u0zxYtuqAHETDR5Q6dG1RwNwz7NUKjsDDHAsBV3KkiiJBtJqjbzy1XU1L41w1g==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis-common/node_modules/gtoken": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", + "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/googleapis-common/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/googleapis-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/googleapis/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/googleapis/node_modules/gaxios": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", + "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis/node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis/node_modules/google-auth-library": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.4.1.tgz", + "integrity": "sha512-Chs7cuzDuav8W/BXOoRgSXw4u0zxYtuqAHETDR5Q6dG1RwNwz7NUKjsDDHAsBV3KkiiJBtJqjbzy1XU1L41w1g==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis/node_modules/gtoken": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", + "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/googleapis/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -25575,7 +25824,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -27215,7 +27463,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, "dependencies": { "universalify": "^2.0.0" }, @@ -30145,7 +30392,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -30436,6 +30682,14 @@ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" }, + "node_modules/monday-sdk-js": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/monday-sdk-js/-/monday-sdk-js-0.5.2.tgz", + "integrity": "sha512-ZxlfnuogPN966e3OcRuZmML/jedhLZTW0h6G00alXejTzKS7avR6z9npco8b++up6Cx0gJfyaQ9cXTTM6+sU1Q==", + "dependencies": { + "node-fetch": "^2.6.0" + } + }, "node_modules/motion": { "version": "10.16.2", "resolved": "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz", @@ -30472,9 +30726,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/msgpackr": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.0.tgz", - "integrity": "sha512-rVQ5YAQDoZKZLX+h8tNq7FiHrPJoeGHViz3U4wIcykhAEpwF/nH2Vbk8dQxmpX5JavkI8C7pt4bnkJ02ZmRoUw==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.1.tgz", + "integrity": "sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==", "optionalDependencies": { "msgpackr-extract": "^3.0.2" } @@ -31008,9 +31262,9 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node_modules/node-abi": { - "version": "3.52.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.52.0.tgz", - "integrity": "sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==", + "version": "3.54.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz", + "integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==", "dependencies": { "semver": "^7.3.5" }, @@ -32639,7 +32893,6 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, "dependencies": { "lru-cache": "^9.1.1 || ^10.0.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -32655,10 +32908,6 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, "engines": { "node": "14 || >=16.14" } @@ -34642,9 +34891,9 @@ } }, "node_modules/preact": { - "version": "10.19.2", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.2.tgz", - "integrity": "sha512-UA9DX/OJwv6YwP9Vn7Ti/vF80XL+YA5H2l7BpCtUr3ya8LWHFzpiO5R+N7dN16ujpIxhekRFuOOF82bXX7K/lg==", + "version": "10.19.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz", + "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -37755,6 +38004,20 @@ "simple-concat": "^1.0.0" } }, + "node_modules/simple-git": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.21.0.tgz", + "integrity": "sha512-oTzw9248AF5bDTMk9MrxsRzEzivMlY+DWH0yWS4VYpMhNLhDWnN06pCtaUyPnqv/FpsdeNmRqmZugMABHRPdDA==", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -39074,7 +39337,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -39088,7 +39350,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -39117,7 +39378,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -40904,19 +41164,19 @@ "integrity": "sha512-v3UJF8xm68BBj6AF4oQML3ikrfK2c9EmZUyLOfShpJuItAqVBHWP/KtpGinkSsIiP6EZyyb6Z3NXyW9dgS9X1w==" }, "node_modules/typeorm": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.17.tgz", - "integrity": "sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.18.tgz", + "integrity": "sha512-St/k5Rdk3Qq0aiYiUw/FRq5i+VqPrqn4ld6m1fqfYJ6uIIK26KFFU9eE0Vl1zdLHkCxTW6Sj4JAL5I3B5N1/5g==", "dependencies": { "@sqltools/formatter": "^1.2.5", "app-root-path": "^3.1.0", "buffer": "^6.0.3", "chalk": "^4.1.2", "cli-highlight": "^2.1.11", - "date-fns": "^2.29.3", + "dayjs": "^1.11.9", "debug": "^4.3.4", "dotenv": "^16.0.3", - "glob": "^8.1.0", + "glob": "^10.3.10", "mkdirp": "^2.1.3", "reflect-metadata": "^0.1.13", "sha.js": "^2.4.11", @@ -40938,13 +41198,13 @@ "peerDependencies": { "@google-cloud/spanner": "^5.18.0", "@sap/hana-client": "^2.12.25", - "better-sqlite3": "^7.1.2 || ^8.0.0", + "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", "hdb-pool": "^0.1.6", "ioredis": "^5.0.4", - "mongodb": "^5.2.0", - "mssql": "^9.1.1", + "mongodb": "^5.8.0", + "mssql": "^9.1.1 || ^10.0.1", "mysql2": "^2.2.5 || ^3.0.1", - "oracledb": "^5.1.0", + "oracledb": "^6.3.0", "pg": "^8.5.1", "pg-native": "^3.0.0", "pg-query-stream": "^4.0.0", @@ -41008,6 +41268,14 @@ } } }, + "node_modules/typeorm/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/typeorm/node_modules/dotenv": { "version": "16.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", @@ -41019,6 +41287,41 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/typeorm/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typeorm/node_modules/mkdirp": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", @@ -41296,7 +41599,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, "engines": { "node": ">= 10.0.0" } @@ -41521,6 +41823,11 @@ "requires-port": "^1.0.0" } }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -41537,19 +41844,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/utf7": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/utf7/-/utf7-1.0.2.tgz", @@ -43030,7 +43324,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", diff --git a/package.json b/package.json index d43103738..b8d46de28 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,16 @@ { "name": "activepieces", - "version": "0.14.3", - "rcVersion": "0.15.0-rc.8", + "version": "0.16.0", + "rcVersion": "0.17.0-rc.1", "scripts": { "prepare": "husky install", "serve:frontend": "nx serve ui-core", "serve:backend": "nx serve backend", "serve:engine": "nx serve engine", "serve:pieces": "turbowatch turbowatch.ts", - "dev": "FORCE_COLOR=true concurrently -n \"GUI,API,ENG,PCS\" -c \"bgBlue.bold,bgGreen.bold,bgRed.bold,bgYellow.bold\" \"npm:serve:frontend\" \"npm:serve:backend\" \"npm:serve:engine\" \"npm:serve:pieces\"", - "dev:backend": "FORCE_COLOR=true concurrently -n \"API,ENG\" -c \"bgGreen.bold,bgRed.bold\" \"npm:serve:backend\" \"npm:serve:engine\"", - "dev:frontend": "FORCE_COLOR=true concurrently -n \"GUI,API,ENG\" -c \"bgBlue.bold,bgGreen.bold,bgRed.bold\" \"npm:serve:frontend\" \"npm:serve:backend\" \"npm:serve:engine\"", + "dev": "cross-env FORCE_COLOR=true && concurrently -n \"GUI,API,ENG,PCS\" -c \"bgBlue.bold,bgGreen.bold,bgRed.bold,bgYellow.bold\" \"npm:serve:frontend\" \"npm:serve:backend\" \"npm:serve:engine\" \"npm:serve:pieces\"", + "dev:backend": "cross-env FORCE_COLOR=true && concurrently -n \"API,ENG\" -c \"bgGreen.bold,bgRed.bold\" \"npm:serve:backend\" \"npm:serve:engine\"", + "dev:frontend": "cross-env FORCE_COLOR=true && concurrently -n \"GUI,API,ENG\" -c \"bgBlue.bold,bgGreen.bold,bgRed.bold\" \"npm:serve:frontend\" \"npm:serve:backend\" \"npm:serve:engine\"", "start": "npm i && npm run dev", "publish-piece": "npx ts-node tools/scripts/utils/publish-piece.ts", "create-piece": "npx ts-node tools/scripts/generate-new-piece.ts" @@ -100,6 +100,7 @@ "contentful-management": "10.42.0", "cron-validator": "1.3.1", "cronstrue": "2.31.0", + "cross-env": "7.0.3", "crypto-js": "4.2.0", "dayjs": "1.11.9", "decompress": "4.2.1", @@ -116,7 +117,9 @@ "firebase-scrypt": "2.2.0", "font-awesome": "4.7.0", "form-data": "4.0.0", + "fs-extra": "11.2.0", "google-auth-library": "8.9.0", + "googleapis": "129.0.0", "http-status-codes": "2.2.0", "imap": "0.8.19", "import-fresh": "3.3.0", @@ -137,6 +140,7 @@ "marked": "4.3.0", "mime-types": "2.1.35", "monaco-editor": "0.34.1", + "monday-sdk-js": "0.5.2", "mustache": "4.2.0", "nanoid": "3.3.6", "ngx-autosize": "2.0.4", @@ -169,6 +173,7 @@ "rss-parser": "3.13.0", "rxjs": "7.8.1", "shade-generator": "1.2.7", + "simple-git": "3.21.0", "showdown": "2.1.0", "snarkjs": "0.7.2", "soap": "1.0.0", @@ -181,8 +186,8 @@ "tsconfig-paths": "4.2.0", "tslib": "2.6.2", "twitter-api-v2": "1.15.1", - "typeorm": "0.3.17", "viem": "1.12.2", + "typeorm": "0.3.18", "xml2js": "0.6.2", "zone.js": "0.13.1" }, diff --git a/packages/backend/.env.tests b/packages/backend/.env.tests index 2f99da106..84cd8b22f 100644 --- a/packages/backend/.env.tests +++ b/packages/backend/.env.tests @@ -14,6 +14,7 @@ AP_ENRICH_ERROR_CONTEXT=true AP_TRIGGER_DEFAULT_POLL_INTERVAL=1 AP_CACHE_PATH=/tmp AP_EDITION=cloud +AP_EXECUTION_MODE=UNSANDBOXED AP_PIECES_SOURCE=DB AP_JWT_SECRET=secret AP_BILLING_SETTINGS={\"nickname\":\"test-flow-plan\",\"tasks\":1000,\"activeFlows\":20,\"minimumPollingInterval\":5,\"connections\":50,\"teamMembers\":1,\"type\":\"FLOWS\"} @@ -25,4 +26,6 @@ AP_SMTP_PORT=SMTP_PORT AP_SMTP_USERNAME=SMTP_USERNAME AP_SMTP_USE_SSL=SMTP_USE_SSL AP_SIGN_UP_ENABLED=false -AP_CLOUD_PLATFORM_ID='cloud-id' \ No newline at end of file +AP_API_KEY="api-key" +AP_CLOUD_PLATFORM_ID="cloud-id" +AP_APPSUMO_TOKEN="app-sumo-token" diff --git a/packages/backend/project.json b/packages/backend/project.json index 1ca27904f..c299f9e20 100644 --- a/packages/backend/project.json +++ b/packages/backend/project.json @@ -81,8 +81,9 @@ "executor": "nx:run-commands", "options": { "commands": [ - "export $(cat packages/backend/.env.tests | xargs) && AP_EDITION=ce nx test-ce backend --output-style stream-without-prefixes", + "nx build backend", "export $(cat packages/backend/.env.tests | xargs) && AP_EDITION=cloud nx test-cloud backend --output-style stream-without-prefixes", + "export $(cat packages/backend/.env.tests | xargs) && AP_EDITION=ce nx test-ce backend --output-style stream-without-prefixes", "export $(cat packages/backend/.env.tests | xargs) && AP_EDITION=ee nx test-ee backend --output-style stream-without-prefixes" ], "parallel": false diff --git a/packages/backend/src/app/app-connection/app-connection-service/app-connection-service.ts b/packages/backend/src/app/app-connection/app-connection-service/app-connection-service.ts index 5411268d9..9cd839761 100644 --- a/packages/backend/src/app/app-connection/app-connection-service/app-connection-service.ts +++ b/packages/backend/src/app/app-connection/app-connection-service/app-connection-service.ts @@ -118,7 +118,7 @@ export const appConnectionService = { async list({ projectId, - appName, + pieceName, cursorRequest, limit, }: ListParams): Promise> { @@ -137,8 +137,8 @@ export const appConnectionService = { const querySelector: Record = { projectId, } - if (!isNil(appName)) { - querySelector.appName = appName + if (!isNil(pieceName)) { + querySelector.pieceName = pieceName } const queryBuilder = repo .createQueryBuilder('app_connection') @@ -178,7 +178,7 @@ const validateConnectionValue = async ( case AppConnectionType.PLATFORM_OAUTH2: return oauth2Handler[connection.value.type].claim({ projectId, - pieceName: connection.appName, + pieceName: connection.pieceName, request: { grantType: OAuth2GrantType.AUTHORIZATION_CODE, code: connection.value.code, @@ -192,7 +192,7 @@ const validateConnectionValue = async ( case AppConnectionType.CLOUD_OAUTH2: return oauth2Handler[connection.value.type].claim({ projectId, - pieceName: connection.appName, + pieceName: connection.pieceName, request: { grantType: OAuth2GrantType.AUTHORIZATION_CODE, code: connection.value.code, @@ -205,7 +205,7 @@ const validateConnectionValue = async ( case AppConnectionType.OAUTH2: return oauth2Handler[connection.value.type].claim({ projectId, - pieceName: connection.appName, + pieceName: connection.pieceName, request: { code: connection.value.code, clientId: connection.value.client_id, @@ -222,7 +222,7 @@ const validateConnectionValue = async ( case AppConnectionType.BASIC_AUTH: case AppConnectionType.SECRET_TEXT: await engineValidateAuth({ - pieceName: connection.appName, + pieceName: connection.pieceName, projectId, auth: connection.value, }) @@ -357,21 +357,21 @@ async function refresh(connection: AppConnection): Promise { switch (connection.value.type) { case AppConnectionType.PLATFORM_OAUTH2: connection.value = await oauth2Handler[connection.value.type].refresh({ - pieceName: connection.appName, + pieceName: connection.pieceName, projectId: connection.projectId, connectionValue: connection.value, }) break case AppConnectionType.CLOUD_OAUTH2: connection.value = await oauth2Handler[connection.value.type].refresh({ - pieceName: connection.appName, + pieceName: connection.pieceName, projectId: connection.projectId, connectionValue: connection.value, }) break case AppConnectionType.OAUTH2: connection.value = await oauth2Handler[connection.value.type].refresh({ - pieceName: connection.appName, + pieceName: connection.pieceName, projectId: connection.projectId, connectionValue: connection.value, }) @@ -405,7 +405,7 @@ type DeleteParams = { type ListParams = { projectId: ProjectId - appName: string | undefined + pieceName: string | undefined cursorRequest: Cursor | null limit: number } diff --git a/packages/backend/src/app/app-connection/app-connection-service/oauth2/services/cloud-oauth2-service.ts b/packages/backend/src/app/app-connection/app-connection-service/oauth2/services/cloud-oauth2-service.ts index e0d8d7500..26cbbebb4 100644 --- a/packages/backend/src/app/app-connection/app-connection-service/oauth2/services/cloud-oauth2-service.ts +++ b/packages/backend/src/app/app-connection/app-connection-service/oauth2/services/cloud-oauth2-service.ts @@ -50,7 +50,7 @@ async function claim({ request, pieceName }: ClaimOAuth2Request): Promise> => { - const { appName, cursor, limit } = request.query + const { pieceName, cursor, limit } = request.query const appConnections = await appConnectionService.list({ projectId: request.principal.projectId, - appName, + pieceName, cursorRequest: cursor ?? null, limit: limit ?? DEFAULT_PAGE_SIZE, }) diff --git a/packages/backend/src/app/app-connection/app-connection.entity.ts b/packages/backend/src/app/app-connection/app-connection.entity.ts index fdc58b378..8561a69ad 100644 --- a/packages/backend/src/app/app-connection/app-connection.entity.ts +++ b/packages/backend/src/app/app-connection/app-connection.entity.ts @@ -19,7 +19,7 @@ export const AppConnectionEntity = new EntitySchema({ type: String, default: AppConnectionStatus.ACTIVE, }, - appName: { + pieceName: { type: String, }, projectId: ApIdSchema, diff --git a/packages/backend/src/app/app.ts b/packages/backend/src/app/app.ts index 73df6b606..02a51ef6d 100644 --- a/packages/backend/src/app/app.ts +++ b/packages/backend/src/app/app.ts @@ -68,12 +68,13 @@ import { billingModule } from './ee/billing/billing/billing.module' import { federatedAuthModule } from './ee/authentication/federated-authn/federated-authn-module' import fastifyFavicon from 'fastify-favicon' import { ProjectMember, ProjectWithUsageAndPlanResponse } from '@activepieces/ee-shared' -import { authorizationMiddleware } from './authentication/authorization-middleware' import { apiKeyModule } from './ee/api-keys/api-key-module' import { domainHelper } from './helper/domain-helper' import { platformDomainHelper } from './ee/helper/platform-domain-helper' import { enterpriseUserModule } from './ee/user/enterprise-user-module' import { flowResponseWatcher } from './flows/flow-run/flow-response-watcher' +import { gitRepoModule } from './ee/git-repos/git-repo.module' +import { securityHandlerChain } from './core/security/security-handler-chain' import { communityFlowTemplateModule } from './flow-templates/community-flow-template.module' export const setupApp = async (): Promise => { @@ -163,7 +164,7 @@ export const setupApp = async (): Promise => { } }) - app.addHook('preHandler', authorizationMiddleware) + app.addHook('preHandler', securityHandlerChain) app.addHook('preHandler', rbacAuthMiddleware) app.setErrorHandler(errorHandler) await app.register(fileModule) @@ -265,6 +266,7 @@ catchCode() await app.register(apiKeyModule) await app.register(enterpriseUserModule) await app.register(platformFlowTemplateModule) + await app.register(gitRepoModule) setPlatformOAuthService({ service: platformOAuth2Service, }) @@ -293,6 +295,7 @@ catchCode() await app.register(apiKeyModule) await app.register(enterpriseUserModule) await app.register(platformFlowTemplateModule) + await app.register(gitRepoModule) setPlatformOAuthService({ service: platformOAuth2Service, }) diff --git a/packages/backend/src/app/authentication/authentication-service/hooks/authentication-service-hooks.ts b/packages/backend/src/app/authentication/authentication-service/hooks/authentication-service-hooks.ts index 2c45e273b..5218f5105 100644 --- a/packages/backend/src/app/authentication/authentication-service/hooks/authentication-service-hooks.ts +++ b/packages/backend/src/app/authentication/authentication-service/hooks/authentication-service-hooks.ts @@ -1,6 +1,7 @@ import { Project, User } from '@activepieces/shared' export type AuthenticationServiceHooks = { + preSignIn(p: PreParams): Promise preSignUp(p: PreParams): Promise postSignUp(p: PostParams): Promise postSignIn(p: PostParams): Promise diff --git a/packages/backend/src/app/authentication/authentication-service/hooks/community-service-hooks.ts b/packages/backend/src/app/authentication/authentication-service/hooks/community-service-hooks.ts index 369f735ba..307eaf72f 100644 --- a/packages/backend/src/app/authentication/authentication-service/hooks/community-service-hooks.ts +++ b/packages/backend/src/app/authentication/authentication-service/hooks/community-service-hooks.ts @@ -1,9 +1,12 @@ -import { PrincipalType, Project, ProjectType, User } from '@activepieces/shared' +import { PrincipalType, Project, ProjectType, User } from '@activepieces/shared' import { projectService } from '../../../project/project-service' import { AuthenticationServiceHooks } from './authentication-service-hooks' import { accessTokenManager } from '../../lib/access-token-manager' export const communityAuthenticationServiceHooks: AuthenticationServiceHooks = { + async preSignIn() { + // Empty + }, async preSignUp() { // Empty }, diff --git a/packages/backend/src/app/authentication/authentication-service/index.ts b/packages/backend/src/app/authentication/authentication-service/index.ts index 3890eb7b3..4205117c7 100644 --- a/packages/backend/src/app/authentication/authentication-service/index.ts +++ b/packages/backend/src/app/authentication/authentication-service/index.ts @@ -35,10 +35,7 @@ export const authenticationService = { if (!params.skipAsserting) await assertSignUpIsEnabled() - await hooks.get().preSignUp({ - email: params.email, - platformId: params.platformId, - }) + await hooks.get().preSignUp(params) const user = await createUser(params) return this.signUpResponse({ @@ -48,6 +45,7 @@ export const authenticationService = { }, async signIn(request: SignInParams): Promise { + await hooks.get().preSignIn(request) const user = await userService.getByPlatformAndEmail({ platformId: request.platformId, email: request.email, @@ -80,7 +78,7 @@ export const authenticationService = { return this.signUp({ email: params.email, - status: params.userStatus, + verified: params.verified, firstName: params.firstName, lastName: params.lastName, trackEvents: true, @@ -143,7 +141,8 @@ const createUser = async (params: SignUpParams): Promise => { try { const newUser: NewUser = { email: params.email, - status: params.status, + verified: params.verified, + status: UserStatus.ACTIVE, firstName: params.firstName, lastName: params.lastName, trackEvents: params.trackEvents, @@ -176,8 +175,15 @@ const assertUserIsAllowedToSignIn: (user: User | null) => asserts user is User = params: null, }) } - - if (user.status !== UserStatus.VERIFIED) { + if (user.status === UserStatus.INACTIVE) { + throw new ActivepiecesError({ + code: ErrorCode.USER_IS_INACTIVE, + params: { + email: user.email, + }, + }) + } + if (!user.verified) { throw new ActivepiecesError({ code: ErrorCode.EMAIL_IS_NOT_VERIFIED, params: { @@ -259,7 +265,7 @@ type SignUpParams = { lastName: string trackEvents: boolean newsLetter: boolean - status: UserStatus + verified: boolean platformId: string | null referringUserId?: string skipAsserting?: boolean @@ -278,7 +284,7 @@ type AssertPasswordsMatchParams = { type FederatedAuthnParams = { email: string - userStatus: UserStatus + verified: boolean firstName: string lastName: string platformId: string | null diff --git a/packages/backend/src/app/authentication/authentication.controller.ts b/packages/backend/src/app/authentication/authentication.controller.ts index b452a944c..e7165e410 100755 --- a/packages/backend/src/app/authentication/authentication.controller.ts +++ b/packages/backend/src/app/authentication/authentication.controller.ts @@ -1,9 +1,9 @@ import { ALL_PRINICPAL_TYPES, ApEdition, ExternalServiceAuthRequest, ExternalUserAuthRequest, ExternalUserRequest, PrincipalType, SignInRequest, SignUpRequest, UserStatus } from '@activepieces/shared' +import { authenticationService } from './authentication-service' import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' import { StatusCodes } from 'http-status-codes' import { resolvePlatformIdForRequest } from '../ee/platform/lib/platform-utils' import { getEdition } from '../helper/secret-helper' -import { authenticationService } from './authentication-service' const edition = getEdition() @@ -13,7 +13,7 @@ export const authenticationController: FastifyPluginAsyncTypebox = async (app) = return authenticationService.signUp({ ...request.body, - status: edition === ApEdition.COMMUNITY ? UserStatus.VERIFIED : UserStatus.CREATED, + verified: edition === ApEdition.COMMUNITY, platformId, skipAsserting: undefined }) @@ -32,19 +32,17 @@ export const authenticationController: FastifyPluginAsyncTypebox = async (app) = app.post( '/external/user/inject', { + config: { + allowedPrincipals: [PrincipalType.EXTERNAL], + }, schema: { body: ExternalUserRequest, }, }, async (request, reply) => { - if (request.principal.type !== PrincipalType.EXTERNAL) { - reply.status(StatusCodes.FORBIDDEN) - return - } - return authenticationService.federatedAuthn({ email: request.body.id, - userStatus: UserStatus.VERIFIED, + verified: true, firstName: request.body.firstName, lastName: request.body.lastName, platformId: null, @@ -56,20 +54,17 @@ export const authenticationController: FastifyPluginAsyncTypebox = async (app) = app.post( '/external/user/auth', { + config: { + allowedPrincipals: [PrincipalType.EXTERNAL], + }, schema: { body: ExternalUserAuthRequest, }, }, async (request, reply) => { - if (request.principal.type !== PrincipalType.EXTERNAL) { - reply.status(StatusCodes.FORBIDDEN) - return - } - console.log('auth EXTERNAL') - return authenticationService.federatedAuthn({ email: request.body.id, - userStatus: UserStatus.VERIFIED, + verified: true, firstName: '', // SHOULD BE AVAILABLE IN PREVIOUSLY INJECTED USER lastName: '', // SHOULD BE AVAILABLE IN PREVIOUSLY INJECTED USER platformId: null, diff --git a/packages/backend/src/app/authentication/authorization-middleware.ts b/packages/backend/src/app/authentication/authorization-middleware.ts deleted file mode 100755 index 26b31e8ed..000000000 --- a/packages/backend/src/app/authentication/authorization-middleware.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { FastifyRequest } from 'fastify' -import { accessTokenManager } from './lib/access-token-manager' -import { ActivepiecesError, EndpointScope, ErrorCode, PlatformRole, Principal, PrincipalType, ProjectType, apId, isEmpty, isNil } from '@activepieces/shared' -import { logger } from '@sentry/utils' -import { system } from '../helper/system/system' -import { SystemProp } from '../helper/system/system-prop' -import { apiKeyService } from '../ee/api-keys/api-key-service' -import { FlowEntity } from '../flows/flow/flow.entity' -import { AppConnectionEntity } from '../app-connection/app-connection.entity' -import { ProjectMemberEntity } from '../ee/project-members/project-member.entity' -import { databaseConnection } from '../database/database-connection' -import { extractResourceName } from './authorization' -import { projectService } from '../project/project-service' -import { nanoid } from 'nanoid' - -const HEADER_PREFIX = 'Bearer ' -const PLATFORM_API_PREFIX = 'sk-' -const API_KEY = system.get(SystemProp.API_KEY) - -export const authorizationMiddleware = async (request: FastifyRequest): Promise => { - const isGlobalApiKeyRoute = await isGlobalApiKey(request) - if (isGlobalApiKeyRoute) { - handleGlobalApiKey(request) - return - } - - const principal = await getPrincipal(request) - const authenticatedRoute = isAuthenticatedRoute(request) - request.principal = principal - - if (principal.type === PrincipalType.UNKNOWN && authenticatedRoute) { - handleInvalidBearerToken() - return - } -} - -async function isGlobalApiKey(request: FastifyRequest): Promise { - return [ - { method: 'POST', url: '/v1/admin/pieces' }, - { method: 'POST', url: '/v1/admin/flow-templates' }, - { method: 'DELETE', url: '/v1/admin/flow-templates' }, - { method: 'POST', url: '/v1/admin/flow-templates/:id' }, - { method: 'POST', url: '/v1/admin/users' }, - { method: 'POST', url: '/v1/admin/platforms' }, - ].some(f => f.url === request.routerPath && f.method.toUpperCase() === request.method) -} - -async function getPrincipal(request: FastifyRequest): Promise { - const rawToken = request.headers.authorization - if (rawToken) { - try { - const token = rawToken.substring(HEADER_PREFIX.length) - if (token.startsWith(PLATFORM_API_PREFIX)) { - return await getAPIKeyPrincipal(token, request) - } - else { - return await getJwtPrincipal(token, request) - } - } - catch (e) { - logger.warn({ err: e }, 'invalid access token') - } - } - return { - id: `ANONYMOUS_${apId()}`, - type: PrincipalType.UNKNOWN, - projectId: `ANONYMOUS_${apId()}`, - projectType: ProjectType.STANDALONE, - } - -} - -async function getJwtPrincipal(token: string, request: FastifyRequest): Promise { - const principal = await accessTokenManager.extractPrincipal(token) - - // TODO Merge with API key once it's specified for all routes, currenttly we ignore old routes - const allowedPrincipals = request.routeConfig.allowedPrincipals - if (allowedPrincipals && !allowedPrincipals.includes(principal.type)) { - throw new ActivepiecesError({ - code: ErrorCode.AUTHORIZATION, - params: { - message: 'invalid route for principal type', - }, - }) - } - // TODO this is a hack to allow token generation for project id - const exemptedRoutesFromProjectIdCheck = ['/v1/users/projects/:projectId/token'] - if (exemptedRoutesFromProjectIdCheck.includes(request.routerPath)) { - return principal - } - const projectId = getProjectIdFromBodyOrQuery(request) - if (projectId && projectId !== principal.projectId) { - throw new ActivepiecesError({ - code: ErrorCode.AUTHORIZATION, - params: { - message: 'invalid project id', - }, - }) - } - return principal -} -async function getAPIKeyPrincipal(rawToken: string, request: FastifyRequest): Promise { - const apiKey = await apiKeyService.getByValueOrThrow(rawToken) - - // TODO enforce in all other princpals types as well - const allowedPrincipals = request.routeConfig.allowedPrincipals ?? [] - if (!allowedPrincipals.includes(PrincipalType.SERVICE)) { - throw new ActivepiecesError({ - code: ErrorCode.AUTHORIZATION, - params: { - message: 'invalid route for principal type', - }, - }) - } - const scope = request.routeConfig.scope - if (scope === EndpointScope.PLATFORM) { - return { - id: apiKey.id, - type: PrincipalType.SERVICE, - // TODO remove this - projectId: 'ANONYMOUS_' + nanoid(), - projectType: ProjectType.PLATFORM_MANAGED, - platform: { - id: apiKey.platformId, - role: PlatformRole.OWNER, - }, - } - } - const projectId = await getProjectIdFromRequest(request) - if (isNil(projectId)) { - throw new ActivepiecesError({ - code: ErrorCode.AUTHORIZATION, - params: { - message: 'missing project id', - }, - }) - } - const project = await projectService.getOneOrThrow(projectId) - if (project.platformId !== apiKey.platformId) { - throw new ActivepiecesError({ - code: ErrorCode.AUTHORIZATION, - params: { - message: 'invalid project id', - }, - }) - } - - return { - id: apiKey.id, - type: PrincipalType.SERVICE, - projectId: project.id, - projectType: ProjectType.PLATFORM_MANAGED, - platform: { - id: apiKey.platformId, - role: PlatformRole.OWNER, - }, - } -} - -async function getProjectIdFromRequest(request: FastifyRequest): Promise { - if (request.routerPath.includes(':id')) { - const resourceName = extractResourceName(request.routerPath) - const { id } = request.params as { id: string } - return extractProjectIdFromResource(resourceName, id) - } - return getProjectIdFromBodyOrQuery(request) -} - -function getProjectIdFromBodyOrQuery(request: FastifyRequest): string | undefined { - if (isObject(request.body) && 'projectId' in request.body) { - return request.body.projectId as string - } - else if (isObject(request.query) && 'projectId' in request.query) { - return request.query.projectId as string - } - - return undefined -} - -async function extractProjectIdFromResource(resource: string | undefined, id: string): Promise { - const tableName = getTableNameFromResource(resource) - if (isNil(tableName)) { - return undefined - } - const entity = await databaseConnection.getRepository(tableName).findOneBy({ - id, - }) - return entity?.projectId -} - -function getTableNameFromResource(resource: string | undefined): string | undefined { - if (isNil(resource)) { - return undefined - } - switch (resource) { - case 'flows': - return FlowEntity.options.name - case 'app-connections': - return AppConnectionEntity.options.name - case 'project-members': - return ProjectMemberEntity.options.name - } - return undefined -} - -function isAuthenticatedRoute(fastifyRequest: FastifyRequest): boolean { - const allowedPrincipals = fastifyRequest.routeConfig.allowedPrincipals ?? [] - if (allowedPrincipals.includes(PrincipalType.UNKNOWN)) { - return false - } - const ignoredRoutes = new Set([ - '/favicon.ico', - '/v1/docs', - '/redirect', - - // External authentication endpoint - '/v1/authentication/external/service/auth', - ]) - if (ignoredRoutes.has(fastifyRequest.routerPath) || fastifyRequest.routerPath.startsWith('/ui')) { - return false - } - return true -} - -function handleGlobalApiKey(request: FastifyRequest): void { - if (isEmpty(API_KEY) || isNil(API_KEY)) { - throw new ActivepiecesError({ - code: ErrorCode.INVALID_API_KEY, - params: {}, - }) - } - - const requestApiKey = request.headers['api-key'] - const keyNotMatching = API_KEY !== requestApiKey - - if (keyNotMatching) { - throw new ActivepiecesError({ - code: ErrorCode.INVALID_API_KEY, - params: {}, - }) - } -} - -function handleInvalidBearerToken(): void { - throw new ActivepiecesError({ - code: ErrorCode.INVALID_BEARER_TOKEN, - params: { - message: 'invalid access token', - }, - }) -} - -function isObject(obj: unknown): obj is Record { - return typeof obj === 'object' && obj !== null && !Array.isArray(obj) -} diff --git a/packages/backend/src/app/authentication/authorization.ts b/packages/backend/src/app/authentication/authorization.ts index ccdd73518..b8b9385ff 100644 --- a/packages/backend/src/app/authentication/authorization.ts +++ b/packages/backend/src/app/authentication/authorization.ts @@ -1,4 +1,4 @@ -import { ActivepiecesError, ErrorCode, PrincipalType } from '@activepieces/shared' +import { ActivepiecesError, ErrorCode, PrincipalType, isObject } from '@activepieces/shared' import { onRequestHookHandler, preSerializationHookHandler } from 'fastify' import { logger } from '../helper/logger' @@ -61,11 +61,6 @@ export const entitiesMustBeOwnedByCurrentProject: preSerializationHookHandler(obj: T): obj is Exclude { - return typeof obj === 'object' && obj !== null && !Array.isArray(obj) -} - - type SingleEntity = { projectId?: string } diff --git a/packages/backend/src/app/core/request/request-utils.ts b/packages/backend/src/app/core/request/request-utils.ts new file mode 100644 index 000000000..9d00892f0 --- /dev/null +++ b/packages/backend/src/app/core/request/request-utils.ts @@ -0,0 +1,15 @@ +import { FastifyRequest } from 'fastify' +import { isObject } from '@activepieces/shared' + +export const requestUtils = { + extractProjectId(request: FastifyRequest): string | undefined { + if (isObject(request.body) && 'projectId' in request.body) { + return request.body.projectId as string + } + else if (isObject(request.query) && 'projectId' in request.query) { + return request.query.projectId as string + } + + return undefined + }, +} diff --git a/packages/backend/src/app/core/security/authn/access-token-authn-handler.ts b/packages/backend/src/app/core/security/authn/access-token-authn-handler.ts new file mode 100644 index 000000000..316248027 --- /dev/null +++ b/packages/backend/src/app/core/security/authn/access-token-authn-handler.ts @@ -0,0 +1,39 @@ +import { FastifyRequest } from 'fastify' +import { BaseSecurityHandler } from '../security-handler' +import { ActivepiecesError, ErrorCode, isNil } from '@activepieces/shared' +import { accessTokenManager } from '../../../authentication/lib/access-token-manager' + +export class AccessTokenAuthnHandler extends BaseSecurityHandler { + private static readonly HEADER_NAME = 'authorization' + private static readonly HEADER_PREFIX = 'Bearer ' + + protected canHandle(request: FastifyRequest): Promise { + const header = request.headers[AccessTokenAuthnHandler.HEADER_NAME] + const prefix = AccessTokenAuthnHandler.HEADER_PREFIX + const routeMatches = header?.startsWith(prefix) ?? false + return Promise.resolve(routeMatches) + } + + protected async doHandle(request: FastifyRequest): Promise { + const accessToken = this.extractAccessTokenOrThrow(request) + const principal = await accessTokenManager.extractPrincipal(accessToken) + request.principal = principal + } + + private extractAccessTokenOrThrow(request: FastifyRequest): string { + const header = request.headers[AccessTokenAuthnHandler.HEADER_NAME] + const prefix = AccessTokenAuthnHandler.HEADER_PREFIX + const accessToken = header?.substring(prefix.length) + + if (isNil(accessToken)) { + throw new ActivepiecesError({ + code: ErrorCode.AUTHENTICATION, + params: { + message: 'missing access token', + }, + }) + } + + return accessToken + } +} diff --git a/packages/backend/src/app/core/security/authn/anonymous-authn-handler.ts b/packages/backend/src/app/core/security/authn/anonymous-authn-handler.ts new file mode 100644 index 000000000..4038537ad --- /dev/null +++ b/packages/backend/src/app/core/security/authn/anonymous-authn-handler.ts @@ -0,0 +1,24 @@ +import { FastifyRequest } from 'fastify' +import { BaseSecurityHandler } from '../security-handler' +import { Principal, PrincipalType, ProjectType, apId, isNil } from '@activepieces/shared' + +export class AnonymousAuthnHandler extends BaseSecurityHandler { + protected canHandle(_request: FastifyRequest): Promise { + return Promise.resolve(true) + } + + protected doHandle(request: FastifyRequest): Promise { + const principal = request.principal as (Principal | undefined) + + if (isNil(principal)) { + request.principal = { + id: `ANONYMOUS_${apId()}`, + type: PrincipalType.UNKNOWN, + projectId: `ANONYMOUS_${apId()}`, + projectType: ProjectType.STANDALONE, + } + } + + return Promise.resolve() + } +} diff --git a/packages/backend/src/app/core/security/authn/app-sumo-authn-handler.ts b/packages/backend/src/app/core/security/authn/app-sumo-authn-handler.ts new file mode 100644 index 000000000..157354fae --- /dev/null +++ b/packages/backend/src/app/core/security/authn/app-sumo-authn-handler.ts @@ -0,0 +1,24 @@ +import { FastifyRequest } from 'fastify' +import { BaseSecurityHandler } from '../security-handler' +import { Principal, PrincipalType } from '@activepieces/shared' + +const ROUTE_PREFIX = '/v1/appsumo' + +export class AppSumoAuthnHandler extends BaseSecurityHandler { + protected canHandle(request: FastifyRequest): Promise { + const routeMatches = request.routerPath.startsWith(ROUTE_PREFIX) + return Promise.resolve(routeMatches) + } + + protected async doHandle(request: FastifyRequest): Promise { + request.principal = this.generatePrincipal() + } + + private generatePrincipal(): Principal { + return { + id: 'app-sumo', + type: PrincipalType.SERVICE, + projectId: 'app-sumo', + } + } +} diff --git a/packages/backend/src/app/core/security/authn/global-api-key-authn-handler.ts b/packages/backend/src/app/core/security/authn/global-api-key-authn-handler.ts new file mode 100644 index 000000000..a26d2caed --- /dev/null +++ b/packages/backend/src/app/core/security/authn/global-api-key-authn-handler.ts @@ -0,0 +1,35 @@ +import { FastifyRequest } from 'fastify' +import { BaseSecurityHandler } from '../security-handler' +import { system } from '../../../helper/system/system' +import { SystemProp } from '../../../helper/system/system-prop' +import { ActivepiecesError, ErrorCode, PrincipalType, apId, isNil } from '@activepieces/shared' + +export class GlobalApiKeyAuthnHandler extends BaseSecurityHandler { + private static readonly HEADER_NAME = 'api-key' + private static readonly API_KEY = system.get(SystemProp.API_KEY) + + protected canHandle(request: FastifyRequest): Promise { + const routeMatches = request.headers[GlobalApiKeyAuthnHandler.HEADER_NAME] !== undefined + return Promise.resolve(routeMatches) + } + + protected doHandle(request: FastifyRequest): Promise { + const requestApiKey = request.headers[GlobalApiKeyAuthnHandler.HEADER_NAME] + const keyNotMatching = requestApiKey !== GlobalApiKeyAuthnHandler.API_KEY + + if (keyNotMatching || isNil(GlobalApiKeyAuthnHandler.API_KEY)) { + throw new ActivepiecesError({ + code: ErrorCode.INVALID_API_KEY, + params: {}, + }) + } + + request.principal = { + id: `SUPER_USER_${apId()}`, + type: PrincipalType.SUPER_USER, + projectId: `SUPER_USER_${apId()}`, + } + + return Promise.resolve() + } +} diff --git a/packages/backend/src/app/core/security/authn/platform-api-key-authn-handler.ts b/packages/backend/src/app/core/security/authn/platform-api-key-authn-handler.ts new file mode 100644 index 000000000..76e7877a5 --- /dev/null +++ b/packages/backend/src/app/core/security/authn/platform-api-key-authn-handler.ts @@ -0,0 +1,153 @@ +import { FastifyRequest } from 'fastify' +import { BaseSecurityHandler } from '../security-handler' +import { ActivepiecesError, EndpointScope, ErrorCode, PlatformRole, Principal, PrincipalType, Project, ProjectId, ProjectType, isNil, isObject } from '@activepieces/shared' +import { apiKeyService } from '../../../ee/api-keys/api-key-service' +import { nanoid } from 'nanoid' +import { ApiKey } from '@activepieces/ee-shared' +import { projectService } from '../../../project/project-service' +import { AppConnectionEntity } from '../../../app-connection/app-connection.entity' +import { extractResourceName } from '../../../authentication/authorization' +import { databaseConnection } from '../../../database/database-connection' +import { ProjectMemberEntity } from '../../../ee/project-members/project-member.entity' +import { FlowEntity } from '../../../flows/flow/flow.entity' +import { requestUtils } from '../../request/request-utils' + +export class PlatformApiKeyAuthnHandler extends BaseSecurityHandler { + private static readonly HEADER_NAME = 'authorization' + private static readonly HEADER_PREFIX = 'Bearer ' + private static readonly API_KEY_PREFIX = 'sk-' + + protected canHandle(request: FastifyRequest): Promise { + const prefix = `${PlatformApiKeyAuthnHandler.HEADER_PREFIX}${PlatformApiKeyAuthnHandler.API_KEY_PREFIX}` + const routeMatches = request.headers[PlatformApiKeyAuthnHandler.HEADER_NAME]?.startsWith(prefix) ?? false + return Promise.resolve(routeMatches) + } + + protected async doHandle(request: FastifyRequest): Promise { + const apiKeyValue = this.extractApiKeyValue(request) + const apiKey = await apiKeyService.getByValueOrThrow(apiKeyValue) + const principal = await this.createPrincipal(request, apiKey) + request.principal = principal + } + + private extractApiKeyValue(request: FastifyRequest): string { + const header = request.headers[PlatformApiKeyAuthnHandler.HEADER_NAME] + const prefix = PlatformApiKeyAuthnHandler.HEADER_PREFIX + const apiKeyValue = header?.substring(prefix.length) + + if (isNil(apiKeyValue)) { + throw new ActivepiecesError({ + code: ErrorCode.AUTHENTICATION, + params: { + message: 'missing api key', + }, + }) + } + + return apiKeyValue + } + + private async createPrincipal(request: FastifyRequest, apiKey: ApiKey): Promise { + const principal: Principal = { + id: apiKey.id, + type: PrincipalType.SERVICE, + projectId: 'ANONYMOUS_' + nanoid(), + projectType: ProjectType.PLATFORM_MANAGED, + platform: { + id: apiKey.platformId, + role: PlatformRole.OWNER, + }, + } + + if (request.routeConfig.scope === EndpointScope.PLATFORM) { + return principal + } + + try { + const projectId = await this.extractProjectIdOrThrow(request) + const project = await projectService.getOneOrThrow(projectId) + + this.assertApiKeyAndProjectBelongToSamePlatform(project, apiKey) + + principal.projectId = projectId + return principal + } + catch (e) { + throw new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid project id', + }, + }) + } + } + + private async extractProjectIdOrThrow(request: FastifyRequest): Promise { + const projectId = requestUtils.extractProjectId(request) ?? + await this.extractProjectIdFromResource(request) + + if (isNil(projectId)) { + throw new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'missing project id', + }, + }) + } + + return projectId + } + + private async extractProjectIdFromResource(request: FastifyRequest): Promise { + const oneResourceRoute = + request.routerPath.includes(':id') && + isObject(request.params) && + 'id' in request.params && + typeof request.params.id === 'string' + + if (!oneResourceRoute) { + return undefined + } + + const resourceName = extractResourceName(request.routerPath) + const { id } = request.params as { id: string } + return this.getProjectIdFromResource(resourceName, id) + } + + private async getProjectIdFromResource(resource: string | undefined, id: string): Promise { + const tableName = this.getTableNameFromResource(resource) + if (isNil(tableName)) { + return undefined + } + const entity = await databaseConnection.getRepository(tableName).findOneBy({ + id, + }) + return entity?.projectId + } + + private getTableNameFromResource(resource: string | undefined): string | undefined { + if (isNil(resource)) { + return undefined + } + switch (resource) { + case 'flows': + return FlowEntity.options.name + case 'app-connections': + return AppConnectionEntity.options.name + case 'project-members': + return ProjectMemberEntity.options.name + } + return undefined + } + + private assertApiKeyAndProjectBelongToSamePlatform(project: Project, apiKey: ApiKey): void { + if (project.platformId !== apiKey.platformId) { + throw new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid project id', + }, + }) + } + } +} diff --git a/packages/backend/src/app/core/security/authz/principal-type-authz-handler.ts b/packages/backend/src/app/core/security/authz/principal-type-authz-handler.ts new file mode 100644 index 000000000..abc98a825 --- /dev/null +++ b/packages/backend/src/app/core/security/authz/principal-type-authz-handler.ts @@ -0,0 +1,46 @@ +import { FastifyRequest } from 'fastify' +import { BaseSecurityHandler } from '../security-handler' +import { ActivepiecesError, ErrorCode, PrincipalType } from '@activepieces/shared' + +export class PrincipalTypeAuthzHandler extends BaseSecurityHandler { + private static readonly IGNORED_ROUTES = [ + '/favicon.ico', + '/v1/docs', + '/redirect', + '/v1/authentication/external/service/auth', + ] + + private static readonly DEFAULT_ALLOWED_PRINCIPAL_TYPES = [ + PrincipalType.USER, + PrincipalType.WORKER, + PrincipalType.SERVICE, + PrincipalType.EXTERNAL, + ] + + protected canHandle(request: FastifyRequest): Promise { + const requestMatches = + !PrincipalTypeAuthzHandler.IGNORED_ROUTES.includes(request.routerPath) && + !request.routerPath.startsWith('/ui') + + return Promise.resolve(requestMatches) + } + + protected doHandle(request: FastifyRequest): Promise { + const principalType = request.principal.type + const configuredPrincipals = request.routeConfig.allowedPrincipals + const defaultPrincipals = PrincipalTypeAuthzHandler.DEFAULT_ALLOWED_PRINCIPAL_TYPES + const allowedPrincipals = configuredPrincipals ?? defaultPrincipals + const principalTypeNotAllowed = !allowedPrincipals.includes(principalType) + + if (principalTypeNotAllowed) { + throw new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid route for principal type', + }, + }) + } + + return Promise.resolve() + } +} diff --git a/packages/backend/src/app/core/security/authz/project-authz-handler.ts b/packages/backend/src/app/core/security/authz/project-authz-handler.ts new file mode 100644 index 000000000..2325896ff --- /dev/null +++ b/packages/backend/src/app/core/security/authz/project-authz-handler.ts @@ -0,0 +1,32 @@ +import { FastifyRequest } from 'fastify' +import { BaseSecurityHandler } from '../security-handler' +import { ActivepiecesError, ErrorCode } from '@activepieces/shared' +import { requestUtils } from '../../request/request-utils' + +export class ProjectAuthzHandler extends BaseSecurityHandler { + private static readonly IGNORED_ROUTES = [ + '/v1/users/projects/:projectId/token', + '/v1/admin/platforms', + '/v1/admin/pieces', + ] + + protected canHandle(request: FastifyRequest): Promise { + const requestMatches = !ProjectAuthzHandler.IGNORED_ROUTES.includes(request.routerPath) + return Promise.resolve(requestMatches) + } + + protected doHandle(request: FastifyRequest): Promise { + const projectId = requestUtils.extractProjectId(request) + + if (projectId && projectId !== request.principal.projectId) { + throw new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid project id', + }, + }) + } + + return Promise.resolve() + } +} diff --git a/packages/backend/src/app/core/security/security-handler-chain.ts b/packages/backend/src/app/core/security/security-handler-chain.ts new file mode 100644 index 000000000..c0b9dd427 --- /dev/null +++ b/packages/backend/src/app/core/security/security-handler-chain.ts @@ -0,0 +1,53 @@ +import { FastifyRequest } from 'fastify' +import { AccessTokenAuthnHandler } from './authn/access-token-authn-handler' +import { AnonymousAuthnHandler } from './authn/anonymous-authn-handler' +import { GlobalApiKeyAuthnHandler } from './authn/global-api-key-authn-handler' +import { PlatformApiKeyAuthnHandler } from './authn/platform-api-key-authn-handler' +import { PrincipalTypeAuthzHandler } from './authz/principal-type-authz-handler' +import { ProjectAuthzHandler } from './authz/project-authz-handler' +import { Principal } from '@activepieces/shared' +import { AppSumoAuthnHandler } from './authn/app-sumo-authn-handler' + +const AUTHN_HANDLERS = [ + new AppSumoAuthnHandler(), + new GlobalApiKeyAuthnHandler(), + new PlatformApiKeyAuthnHandler(), + new AccessTokenAuthnHandler(), + new AnonymousAuthnHandler(), +] + +const AUTHZ_HANDLERS = [ + new PrincipalTypeAuthzHandler(), + new ProjectAuthzHandler(), +] + +export const securityHandlerChain = async (request: FastifyRequest): Promise => { + await executeAuthnHandlers(request) + await executeAuthzHandlers(request) +} + +/** + * Executes authn handlers in order, if one of the handlers populates the principal, + * the remaining handlers are skipped. + */ +const executeAuthnHandlers = async (request: FastifyRequest): Promise => { + for (const handler of AUTHN_HANDLERS) { + await handler.handle(request) + + const principalPopulated = checkWhetherPrincipalIsPopulated(request) + if (principalPopulated) { + return + } + } +} + +const executeAuthzHandlers = async (request: FastifyRequest): Promise => { + for (const handler of AUTHZ_HANDLERS) { + await handler.handle(request) + } +} + +const checkWhetherPrincipalIsPopulated = (request: FastifyRequest): boolean => { + const principal = request.principal as (Principal | undefined) + return principal !== undefined +} diff --git a/packages/backend/src/app/core/security/security-handler.ts b/packages/backend/src/app/core/security/security-handler.ts new file mode 100644 index 000000000..a9f06498d --- /dev/null +++ b/packages/backend/src/app/core/security/security-handler.ts @@ -0,0 +1,16 @@ +import { FastifyRequest } from 'fastify' + +export type SecurityHandler = { + handle(request: FastifyRequest): Promise +} + +export abstract class BaseSecurityHandler implements SecurityHandler { + async handle(request: FastifyRequest): Promise { + if (await this.canHandle(request)) { + await this.doHandle(request) + } + } + + protected abstract canHandle(request: FastifyRequest): Promise + protected abstract doHandle(request: FastifyRequest): Promise +} diff --git a/packages/backend/src/app/database/database-connection.ts b/packages/backend/src/app/database/database-connection.ts index 25dd86a81..05728e0fb 100755 --- a/packages/backend/src/app/database/database-connection.ts +++ b/packages/backend/src/app/database/database-connection.ts @@ -34,6 +34,7 @@ import { OAuthAppEntity } from '../ee/oauth-apps/oauth-app.entity' import { ProjectPlanEntity } from '../ee/billing/project-plan/project-plan.entity' import { OtpEntity } from '../ee/otp/otp-entity' import { ApiKeyEntity } from '../ee/api-keys/api-key-entity' +import { GitRepoEntity } from '../ee/git-repos/git-repo.entity' const databaseType = system.get(SystemProp.DB_TYPE) @@ -75,6 +76,7 @@ function getEntities(): EntitySchema[] { OAuthAppEntity, OtpEntity, ApiKeyEntity, + GitRepoEntity, ) break case ApEdition.ENTERPRISE: @@ -89,6 +91,7 @@ function getEntities(): EntitySchema[] { OtpEntity, ApiKeyEntity, FlowTemplateEntity, + GitRepoEntity, ) break case ApEdition.COMMUNITY: diff --git a/packages/backend/src/app/database/migration/postgres/1703711596105-RenameAppNameToPieceName.ts b/packages/backend/src/app/database/migration/postgres/1703711596105-RenameAppNameToPieceName.ts new file mode 100644 index 000000000..525ace720 --- /dev/null +++ b/packages/backend/src/app/database/migration/postgres/1703711596105-RenameAppNameToPieceName.ts @@ -0,0 +1,20 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class RenameAppNameToPieceName1703711596105 implements MigrationInterface { + name = 'RenameAppNameToPieceName1703711596105' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "app_connection" + RENAME COLUMN "appName" TO "pieceName" + `) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "app_connection" + RENAME COLUMN "pieceName" TO "appName" + `) + } + +} diff --git a/packages/backend/src/app/database/migration/postgres/1703769034497-AddVerifiedAndChangeStatus.ts b/packages/backend/src/app/database/migration/postgres/1703769034497-AddVerifiedAndChangeStatus.ts new file mode 100644 index 000000000..ab44a2c2d --- /dev/null +++ b/packages/backend/src/app/database/migration/postgres/1703769034497-AddVerifiedAndChangeStatus.ts @@ -0,0 +1,58 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddVerifiedAndChangeStatus1703769034497 implements MigrationInterface { + name = 'AddVerifiedAndChangeStatus1703769034497' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "user" + ADD "verified" boolean + `) + await queryRunner.query(` + UPDATE "user" + SET "verified" = false + `) + + await queryRunner.query(` + ALTER TABLE "user" + ALTER COLUMN "verified" SET NOT NULL + `) + + await queryRunner.query(` + UPDATE "user" + SET "verified" = true + WHERE "status" = 'VERIFIED' + `) + + await queryRunner.query(` + UPDATE "user" + SET "status" = 'ACTIVE' + `) + + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + UPDATE "user" + SET "status" = 'VERIFIED' + WHERE "verified" = true + `) + + await queryRunner.query(` + UPDATE "user" + SET "status" = 'CREATED' + WHERE "verified" = false + `) + + await queryRunner.query(` + ALTER TABLE "user" + ALTER COLUMN "verified" DROP NOT NULL + `) + + await queryRunner.query(` + ALTER TABLE "user" + DROP COLUMN "verified" + `) + } + +} diff --git a/packages/backend/src/app/database/migration/postgres/1704503804056-AddGitRepoMigrationPostgres.ts b/packages/backend/src/app/database/migration/postgres/1704503804056-AddGitRepoMigrationPostgres.ts new file mode 100644 index 000000000..804c53568 --- /dev/null +++ b/packages/backend/src/app/database/migration/postgres/1704503804056-AddGitRepoMigrationPostgres.ts @@ -0,0 +1,41 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddGitRepoMigrationPostgres1704503804056 implements MigrationInterface { + name = 'AddGitRepoMigrationPostgres1704503804056' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE "git_repo" ( + "id" character varying(21) NOT NULL, + "created" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + "updated" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + "projectId" character varying(21) NOT NULL, + "remoteUrl" character varying NOT NULL, + "branch" character varying NOT NULL, + "sshPrivateKey" character varying, + CONSTRAINT "REL_5b59d96420074128fc1d269b9c" UNIQUE ("projectId"), + CONSTRAINT "PK_de881ac6eac39e4d9ba7c5ed3e6" PRIMARY KEY ("id") + ) + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_git_repo_project_id" ON "git_repo" ("projectId") + `) + await queryRunner.query(` + ALTER TABLE "git_repo" + ADD CONSTRAINT "fk_git_repo_project_id" FOREIGN KEY ("projectId") REFERENCES "project"("id") ON DELETE CASCADE ON UPDATE NO ACTION + `) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "git_repo" DROP CONSTRAINT "fk_git_repo_project_id" + `) + await queryRunner.query(` + DROP INDEX "public"."idx_git_repo_project_id" + `) + await queryRunner.query(` + DROP TABLE "git_repo" + `) + } + +} diff --git a/packages/backend/src/app/database/migration/postgres/1704636362533-AddGitSyncEnabledToPlatform.ts b/packages/backend/src/app/database/migration/postgres/1704636362533-AddGitSyncEnabledToPlatform.ts new file mode 100644 index 000000000..3c1af8a38 --- /dev/null +++ b/packages/backend/src/app/database/migration/postgres/1704636362533-AddGitSyncEnabledToPlatform.ts @@ -0,0 +1,29 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddGitSyncEnabledToPlatform1704636362533 implements MigrationInterface { + name = 'AddGitSyncEnabledToPlatform1704636362533' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "platform" + ADD "gitSyncEnabled" boolean + `) + + await queryRunner.query(` + UPDATE "platform" + SET "gitSyncEnabled" = false + `) + + await queryRunner.query(` + ALTER TABLE "platform" + ALTER COLUMN "gitSyncEnabled" SET NOT NULL + `) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "platform" DROP COLUMN "gitSyncEnabled" + `) + } + +} diff --git a/packages/backend/src/app/database/migration/postgres/1704667304953-AddAuthOptionsToPlatform.ts b/packages/backend/src/app/database/migration/postgres/1704667304953-AddAuthOptionsToPlatform.ts new file mode 100644 index 000000000..a43dd15c9 --- /dev/null +++ b/packages/backend/src/app/database/migration/postgres/1704667304953-AddAuthOptionsToPlatform.ts @@ -0,0 +1,73 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddAuthOptionsToPlatform1704667304953 implements MigrationInterface { + name = 'AddAuthOptionsToPlatform1704667304953' + + public async up(queryRunner: QueryRunner): Promise { + + // allowedAuthDomains + await queryRunner.query(` + ALTER TABLE "platform" + ADD COLUMN "allowedAuthDomains" character varying[] + `) + await queryRunner.query(` + UPDATE "platform" + SET "allowedAuthDomains" = '{}'::character varying[] + `) + await queryRunner.query(` + ALTER TABLE "platform" + ALTER COLUMN "allowedAuthDomains" SET NOT NULL + `) + + + // enforceAllowedAuthDomains + await queryRunner.query(` + ALTER TABLE "platform" + ADD COLUMN "enforceAllowedAuthDomains" boolean + `) + await queryRunner.query(` + UPDATE "platform" + SET "enforceAllowedAuthDomains" = false + `) + await queryRunner.query(` + ALTER TABLE "platform" + ALTER COLUMN "enforceAllowedAuthDomains" SET NOT NULL + `) + + // ssoEnabled + await queryRunner.query(` + ALTER TABLE "platform" + ADD COLUMN "ssoEnabled" boolean + `) + await queryRunner.query(` + UPDATE "platform" + SET "ssoEnabled" = false + `) + await queryRunner.query(` + ALTER TABLE "platform" + ALTER COLUMN "ssoEnabled" SET NOT NULL + `) + + // federatedAuthProviders + await queryRunner.query('ALTER TABLE "platform" ADD COLUMN "federatedAuthProviders" jsonb') + await queryRunner.query('UPDATE "platform" SET "federatedAuthProviders" = \'{}\'') + await queryRunner.query('ALTER TABLE "platform" ALTER COLUMN "federatedAuthProviders" SET NOT NULL') + + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "platform" DROP COLUMN "federatedAuthProviders" + `) + await queryRunner.query(` + ALTER TABLE "platform" DROP COLUMN "ssoEnabled" + `) + await queryRunner.query(` + ALTER TABLE "platform" DROP COLUMN "enforceAllowedAuthDomains" + `) + await queryRunner.query(` + ALTER TABLE "platform" DROP COLUMN "allowedAuthDomains" + `) + } + +} diff --git a/packages/backend/src/app/database/migration/postgres/1704797979825-AddEnableEmailAuthToPlatform.ts b/packages/backend/src/app/database/migration/postgres/1704797979825-AddEnableEmailAuthToPlatform.ts new file mode 100644 index 000000000..fec9f66f4 --- /dev/null +++ b/packages/backend/src/app/database/migration/postgres/1704797979825-AddEnableEmailAuthToPlatform.ts @@ -0,0 +1,18 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddEnableEmailAuthToPlatform1704797979825 implements MigrationInterface { + name = 'AddEnableEmailAuthToPlatform1704797979825' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query('ALTER TABLE "platform" ADD "emailAuthEnabled" boolean') + await queryRunner.query('UPDATE "platform" SET "emailAuthEnabled" = TRUE') + await queryRunner.query('ALTER TABLE "platform" ALTER COLUMN "emailAuthEnabled" SET NOT NULL') + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "platform" DROP COLUMN "emailAuthEnabled" + `) + } + +} diff --git a/packages/backend/src/app/database/migration/1703713027818-UpdateStatusInUserSqlite.ts b/packages/backend/src/app/database/migration/sqlite/1703713027818-UpdateStatusInUserSqlite.ts similarity index 99% rename from packages/backend/src/app/database/migration/1703713027818-UpdateStatusInUserSqlite.ts rename to packages/backend/src/app/database/migration/sqlite/1703713027818-UpdateStatusInUserSqlite.ts index e263b9751..3e11f0813 100644 --- a/packages/backend/src/app/database/migration/1703713027818-UpdateStatusInUserSqlite.ts +++ b/packages/backend/src/app/database/migration/sqlite/1703713027818-UpdateStatusInUserSqlite.ts @@ -1,9 +1,11 @@ import { MigrationInterface, QueryRunner } from 'typeorm' +import { logger } from '../../../helper/logger' export class UpdateStatusInUserSqlite1703713027818 implements MigrationInterface { name = 'UpdateStatusInUserSqlite1703713027818' public async up(queryRunner: QueryRunner): Promise { + logger.info('UpdateStatusInUserSqlite1703713027818 up') await queryRunner.query(` DROP INDEX "idx_user_platform_id_email" `) diff --git a/packages/backend/src/app/database/migration/sqlite/1703713475755-RenameAppNameToPieceNameSqlite.ts b/packages/backend/src/app/database/migration/sqlite/1703713475755-RenameAppNameToPieceNameSqlite.ts new file mode 100644 index 000000000..445ab188a --- /dev/null +++ b/packages/backend/src/app/database/migration/sqlite/1703713475755-RenameAppNameToPieceNameSqlite.ts @@ -0,0 +1,540 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' +import { logger } from '../../../helper/logger' + +export class RenameAppNameToPieceNameSqlite1703713475755 implements MigrationInterface { + name = 'RenameAppNameToPieceNameSqlite1703713475755' + + public async up(queryRunner: QueryRunner): Promise { + logger.info('RenameAppNameToPieceNameSqlite1703713475755 up') + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_email" + `) + await queryRunner.query(` + DROP INDEX "idx_user_partial_unique_email_platform_id_is_null" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_external_id" + `) + await queryRunner.query(` + CREATE TABLE "temporary_user" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "email" varchar NOT NULL, + "firstName" varchar NOT NULL, + "lastName" varchar NOT NULL, + "password" varchar NOT NULL, + "status" varchar NOT NULL, + "trackEvents" boolean, + "newsLetter" boolean, + "imageUrl" varchar, + "title" varchar, + "externalId" varchar, + "platformId" varchar, + CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email") + ) + `) + await queryRunner.query(` + INSERT INTO "temporary_user"( + "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + ) + SELECT "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + FROM "user" + `) + await queryRunner.query(` + DROP TABLE "user" + `) + await queryRunner.query(` + ALTER TABLE "temporary_user" + RENAME TO "user" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_email" ON "user" ("platformId", "email") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_partial_unique_email_platform_id_is_null" ON "user" ("email") + WHERE "platformId" IS NULL + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_external_id" ON "user" ("platformId", "externalId") + `) + await queryRunner.query(` + DROP INDEX "idx_app_connection_project_id_and_name" + `) + await queryRunner.query(` + CREATE TABLE "temporary_app_connection" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "name" varchar NOT NULL, + "pieceName" varchar NOT NULL, + "projectId" varchar(21) NOT NULL, + "value" text NOT NULL, + "type" varchar NOT NULL, + "status" varchar NOT NULL DEFAULT ('ACTIVE'), + CONSTRAINT "fk_app_connection_app_project_id" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ) + `) + await queryRunner.query(` + INSERT INTO "temporary_app_connection"( + "id", + "created", + "updated", + "name", + "pieceName", + "projectId", + "value", + "type", + "status" + ) + SELECT "id", + "created", + "updated", + "name", + "appName", + "projectId", + "value", + "type", + "status" + FROM "app_connection" + `) + await queryRunner.query(` + DROP TABLE "app_connection" + `) + await queryRunner.query(` + ALTER TABLE "temporary_app_connection" + RENAME TO "app_connection" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_app_connection_project_id_and_name" ON "app_connection" ("projectId", "name") + `) + await queryRunner.query(` + DROP INDEX "idx_flow_folder_id" + `) + await queryRunner.query(` + DROP INDEX "idx_flow_project_id" + `) + await queryRunner.query(` + CREATE TABLE "temporary_flow" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "projectId" varchar(21) NOT NULL, + "folderId" varchar(21), + "status" varchar CHECK("status" IN ('ENABLED', 'DISABLED')) NOT NULL DEFAULT ('DISABLED'), + "schedule" text, + "publishedVersionId" varchar(21), + CONSTRAINT "UQ_15375936ad7b8c5dc3f50783a22" UNIQUE ("publishedVersionId"), + CONSTRAINT "fk_flow_project_id" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "fk_flow_folder_id" FOREIGN KEY ("folderId") REFERENCES "folder" ("id") ON DELETE + SET NULL ON UPDATE NO ACTION, + CONSTRAINT "fk_flow_published_version" FOREIGN KEY ("publishedVersionId") REFERENCES "flow_version" ("id") ON DELETE RESTRICT ON UPDATE NO ACTION + ) + `) + await queryRunner.query(` + INSERT INTO "temporary_flow"( + "id", + "created", + "updated", + "projectId", + "folderId", + "status", + "schedule", + "publishedVersionId" + ) + SELECT "id", + "created", + "updated", + "projectId", + "folderId", + "status", + "schedule", + "publishedVersionId" + FROM "flow" + `) + await queryRunner.query(` + DROP TABLE "flow" + `) + await queryRunner.query(` + ALTER TABLE "temporary_flow" + RENAME TO "flow" + `) + await queryRunner.query(` + CREATE INDEX "idx_flow_folder_id" ON "flow" ("folderId") + `) + await queryRunner.query(` + CREATE INDEX "idx_flow_project_id" ON "flow" ("projectId") + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_email" + `) + await queryRunner.query(` + DROP INDEX "idx_user_partial_unique_email_platform_id_is_null" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_external_id" + `) + await queryRunner.query(` + CREATE TABLE "temporary_user" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "email" varchar NOT NULL, + "firstName" varchar NOT NULL, + "lastName" varchar NOT NULL, + "password" varchar NOT NULL, + "status" varchar NOT NULL, + "trackEvents" boolean, + "newsLetter" boolean, + "imageUrl" varchar, + "title" varchar, + "externalId" varchar, + "platformId" varchar, + CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email") + ) + `) + await queryRunner.query(` + INSERT INTO "temporary_user"( + "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + ) + SELECT "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + FROM "user" + `) + await queryRunner.query(` + DROP TABLE "user" + `) + await queryRunner.query(` + ALTER TABLE "temporary_user" + RENAME TO "user" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_partial_unique_email_platform_id_is_null" ON "user" ("email") + WHERE "platformId" IS NULL + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_external_id" ON "user" ("platformId", "externalId") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_email" ON "user" ("platformId", "email") + `) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_email" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_external_id" + `) + await queryRunner.query(` + DROP INDEX "idx_user_partial_unique_email_platform_id_is_null" + `) + await queryRunner.query(` + ALTER TABLE "user" + RENAME TO "temporary_user" + `) + await queryRunner.query(` + CREATE TABLE "user" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "email" varchar NOT NULL, + "firstName" varchar NOT NULL, + "lastName" varchar NOT NULL, + "password" varchar NOT NULL, + "status" varchar NOT NULL, + "trackEvents" boolean, + "newsLetter" boolean, + "imageUrl" varchar, + "title" varchar, + "externalId" varchar, + "platformId" varchar, + CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email") + ) + `) + await queryRunner.query(` + INSERT INTO "user"( + "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + ) + SELECT "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + FROM "temporary_user" + `) + await queryRunner.query(` + DROP TABLE "temporary_user" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_external_id" ON "user" ("platformId", "externalId") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_partial_unique_email_platform_id_is_null" ON "user" ("email") + WHERE "platformId" IS NULL + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_email" ON "user" ("platformId", "email") + `) + await queryRunner.query(` + DROP INDEX "idx_flow_project_id" + `) + await queryRunner.query(` + DROP INDEX "idx_flow_folder_id" + `) + await queryRunner.query(` + ALTER TABLE "flow" + RENAME TO "temporary_flow" + `) + await queryRunner.query(` + CREATE TABLE "flow" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "projectId" varchar(21) NOT NULL, + "folderId" varchar(21), + "status" varchar CHECK("status" IN ('ENABLED', 'DISABLED')) NOT NULL DEFAULT ('DISABLED'), + "schedule" text, + "publishedVersionId" varchar(21), + CONSTRAINT "UQ_15375936ad7b8c5dc3f50783a22" UNIQUE ("publishedVersionId"), + CONSTRAINT "fk_flow_project_id" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "fk_flow_folder_id" FOREIGN KEY ("folderId") REFERENCES "folder" ("id") ON DELETE + SET NULL ON UPDATE NO ACTION, + CONSTRAINT "fk_flow_published_version" FOREIGN KEY ("publishedVersionId") REFERENCES "flow_version" ("id") ON DELETE RESTRICT ON UPDATE NO ACTION + ) + `) + await queryRunner.query(` + INSERT INTO "flow"( + "id", + "created", + "updated", + "projectId", + "folderId", + "status", + "schedule", + "publishedVersionId" + ) + SELECT "id", + "created", + "updated", + "projectId", + "folderId", + "status", + "schedule", + "publishedVersionId" + FROM "temporary_flow" + `) + await queryRunner.query(` + DROP TABLE "temporary_flow" + `) + await queryRunner.query(` + CREATE INDEX "idx_flow_project_id" ON "flow" ("projectId") + `) + await queryRunner.query(` + CREATE INDEX "idx_flow_folder_id" ON "flow" ("folderId") + `) + await queryRunner.query(` + DROP INDEX "idx_app_connection_project_id_and_name" + `) + await queryRunner.query(` + ALTER TABLE "app_connection" + RENAME TO "temporary_app_connection" + `) + await queryRunner.query(` + CREATE TABLE "app_connection" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "name" varchar NOT NULL, + "appName" varchar NOT NULL, + "projectId" varchar(21) NOT NULL, + "value" text NOT NULL, + "type" varchar NOT NULL, + "status" varchar NOT NULL DEFAULT ('ACTIVE'), + CONSTRAINT "fk_app_connection_app_project_id" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ) + `) + await queryRunner.query(` + INSERT INTO "app_connection"( + "id", + "created", + "updated", + "name", + "appName", + "projectId", + "value", + "type", + "status" + ) + SELECT "id", + "created", + "updated", + "name", + "pieceName", + "projectId", + "value", + "type", + "status" + FROM "temporary_app_connection" + `) + await queryRunner.query(` + DROP TABLE "temporary_app_connection" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_app_connection_project_id_and_name" ON "app_connection" ("projectId", "name") + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_external_id" + `) + await queryRunner.query(` + DROP INDEX "idx_user_partial_unique_email_platform_id_is_null" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_email" + `) + await queryRunner.query(` + ALTER TABLE "user" + RENAME TO "temporary_user" + `) + await queryRunner.query(` + CREATE TABLE "user" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "email" varchar NOT NULL, + "firstName" varchar NOT NULL, + "lastName" varchar NOT NULL, + "password" varchar NOT NULL, + "status" varchar NOT NULL, + "trackEvents" boolean, + "newsLetter" boolean, + "imageUrl" varchar, + "title" varchar, + "externalId" varchar, + "platformId" varchar, + CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email") + ) + `) + await queryRunner.query(` + INSERT INTO "user"( + "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + ) + SELECT "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + FROM "temporary_user" + `) + await queryRunner.query(` + DROP TABLE "temporary_user" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_external_id" ON "user" ("platformId", "externalId") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_partial_unique_email_platform_id_is_null" ON "user" ("email") + WHERE "platformId" IS NULL + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_email" ON "user" ("platformId", "email") + `) + } + +} diff --git a/packages/backend/src/app/database/migration/sqlite/1703768553820-AddVerifiedAndChangeStatusSqlite.ts b/packages/backend/src/app/database/migration/sqlite/1703768553820-AddVerifiedAndChangeStatusSqlite.ts new file mode 100644 index 000000000..c237b9524 --- /dev/null +++ b/packages/backend/src/app/database/migration/sqlite/1703768553820-AddVerifiedAndChangeStatusSqlite.ts @@ -0,0 +1,453 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddVerifiedAndChangeStatusSqlite1703768553820 implements MigrationInterface { + name = 'AddVerifiedAndChangeStatusSqlite1703768553820' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_email" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_external_id" + `) + await queryRunner.query(` + DROP INDEX "idx_user_partial_unique_email_platform_id_is_null" + `) + await queryRunner.query(` + CREATE TABLE "temporary_user" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "email" varchar NOT NULL, + "firstName" varchar NOT NULL, + "lastName" varchar NOT NULL, + "password" varchar NOT NULL, + "status" varchar NOT NULL, + "trackEvents" boolean, + "newsLetter" boolean, + "imageUrl" varchar, + "title" varchar, + "externalId" varchar, + "platformId" varchar, + "verified" boolean, + CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email") + ) + `) + await queryRunner.query(` + INSERT INTO "temporary_user"( + "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + ) + SELECT "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + FROM "user" + `) + await queryRunner.query(` + DROP TABLE "user" + `) + await queryRunner.query(` + ALTER TABLE "temporary_user" + RENAME TO "user" + `) + + await queryRunner.query(` + UPDATE "user" SET "status" = 'ACTIVE', "verified" = true + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_email" ON "user" ("platformId", "email") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_external_id" ON "user" ("platformId", "externalId") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_partial_unique_email_platform_id_is_null" ON "user" ("email") + WHERE "platformId" IS NULL + `) + await queryRunner.query(` + DROP INDEX "idx_flow_project_id" + `) + await queryRunner.query(` + DROP INDEX "idx_flow_folder_id" + `) + await queryRunner.query(` + CREATE TABLE "temporary_flow" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "projectId" varchar(21) NOT NULL, + "folderId" varchar(21), + "status" varchar CHECK("status" IN ('ENABLED', 'DISABLED')) NOT NULL DEFAULT ('DISABLED'), + "schedule" text, + "publishedVersionId" varchar(21), + CONSTRAINT "UQ_15375936ad7b8c5dc3f50783a22" UNIQUE ("publishedVersionId"), + CONSTRAINT "fk_flow_published_version" FOREIGN KEY ("publishedVersionId") REFERENCES "flow_version" ("id") ON DELETE RESTRICT ON UPDATE NO ACTION, + CONSTRAINT "fk_flow_folder_id" FOREIGN KEY ("folderId") REFERENCES "folder" ("id") ON DELETE + SET NULL ON UPDATE NO ACTION, + CONSTRAINT "fk_flow_project_id" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ) + `) + await queryRunner.query(` + INSERT INTO "temporary_flow"( + "id", + "created", + "updated", + "projectId", + "folderId", + "status", + "schedule", + "publishedVersionId" + ) + SELECT "id", + "created", + "updated", + "projectId", + "folderId", + "status", + "schedule", + "publishedVersionId" + FROM "flow" + `) + await queryRunner.query(` + DROP TABLE "flow" + `) + await queryRunner.query(` + ALTER TABLE "temporary_flow" + RENAME TO "flow" + `) + await queryRunner.query(` + CREATE INDEX "idx_flow_project_id" ON "flow" ("projectId") + `) + await queryRunner.query(` + CREATE INDEX "idx_flow_folder_id" ON "flow" ("folderId") + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_email" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_external_id" + `) + await queryRunner.query(` + DROP INDEX "idx_user_partial_unique_email_platform_id_is_null" + `) + await queryRunner.query(` + CREATE TABLE "temporary_user" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "email" varchar NOT NULL, + "firstName" varchar NOT NULL, + "lastName" varchar NOT NULL, + "password" varchar NOT NULL, + "status" varchar NOT NULL, + "trackEvents" boolean, + "newsLetter" boolean, + "imageUrl" varchar, + "title" varchar, + "externalId" varchar, + "platformId" varchar, + "verified" boolean NOT NULL, + CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email") + ) + `) + await queryRunner.query(` + INSERT INTO "temporary_user"( + "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId", + "verified" + ) + SELECT "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId", + "verified" + FROM "user" + `) + await queryRunner.query(` + DROP TABLE "user" + `) + await queryRunner.query(` + ALTER TABLE "temporary_user" + RENAME TO "user" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_external_id" ON "user" ("platformId", "externalId") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_partial_unique_email_platform_id_is_null" ON "user" ("email") + WHERE "platformId" IS NULL + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_email" ON "user" ("platformId", "email") + `) + + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + UPDATE "user" SET "status" = 'VERIFIED', "verified" = false + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_email" + `) + await queryRunner.query(` + DROP INDEX "idx_user_partial_unique_email_platform_id_is_null" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_external_id" + `) + await queryRunner.query(` + ALTER TABLE "user" + RENAME TO "temporary_user" + `) + await queryRunner.query(` + CREATE TABLE "user" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "email" varchar NOT NULL, + "firstName" varchar NOT NULL, + "lastName" varchar NOT NULL, + "password" varchar NOT NULL, + "status" varchar NOT NULL, + "trackEvents" boolean, + "newsLetter" boolean, + "imageUrl" varchar, + "title" varchar, + "externalId" varchar, + "platformId" varchar, + "verified" boolean NOT NULL, + CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email") + ) + `) + await queryRunner.query(` + INSERT INTO "user"( + "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId", + "verified" + ) + SELECT "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId", + "verified" + FROM "temporary_user" + `) + await queryRunner.query(` + DROP TABLE "temporary_user" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_partial_unique_email_platform_id_is_null" ON "user" ("email") + WHERE "platformId" IS NULL + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_external_id" ON "user" ("platformId", "externalId") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_email" ON "user" ("platformId", "email") + `) + await queryRunner.query(` + DROP INDEX "idx_flow_folder_id" + `) + await queryRunner.query(` + DROP INDEX "idx_flow_project_id" + `) + await queryRunner.query(` + ALTER TABLE "flow" + RENAME TO "temporary_flow" + `) + await queryRunner.query(` + CREATE TABLE "flow" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "projectId" varchar(21) NOT NULL, + "folderId" varchar(21), + "status" varchar CHECK("status" IN ('ENABLED', 'DISABLED')) NOT NULL DEFAULT ('DISABLED'), + "schedule" text, + "publishedVersionId" varchar(21), + CONSTRAINT "UQ_15375936ad7b8c5dc3f50783a22" UNIQUE ("publishedVersionId"), + CONSTRAINT "fk_flow_published_version" FOREIGN KEY ("publishedVersionId") REFERENCES "flow_version" ("id") ON DELETE RESTRICT ON UPDATE NO ACTION, + CONSTRAINT "fk_flow_folder_id" FOREIGN KEY ("folderId") REFERENCES "folder" ("id") ON DELETE + SET NULL ON UPDATE NO ACTION, + CONSTRAINT "fk_flow_project_id" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ) + `) + await queryRunner.query(` + INSERT INTO "flow"( + "id", + "created", + "updated", + "projectId", + "folderId", + "status", + "schedule", + "publishedVersionId" + ) + SELECT "id", + "created", + "updated", + "projectId", + "folderId", + "status", + "schedule", + "publishedVersionId" + FROM "temporary_flow" + `) + await queryRunner.query(` + DROP TABLE "temporary_flow" + `) + await queryRunner.query(` + CREATE INDEX "idx_flow_folder_id" ON "flow" ("folderId") + `) + await queryRunner.query(` + CREATE INDEX "idx_flow_project_id" ON "flow" ("projectId") + `) + await queryRunner.query(` + DROP INDEX "idx_user_partial_unique_email_platform_id_is_null" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_external_id" + `) + await queryRunner.query(` + DROP INDEX "idx_user_platform_id_email" + `) + await queryRunner.query(` + ALTER TABLE "user" + RENAME TO "temporary_user" + `) + await queryRunner.query(` + CREATE TABLE "user" ( + "id" varchar(21) PRIMARY KEY NOT NULL, + "created" datetime NOT NULL DEFAULT (datetime('now')), + "updated" datetime NOT NULL DEFAULT (datetime('now')), + "email" varchar NOT NULL, + "firstName" varchar NOT NULL, + "lastName" varchar NOT NULL, + "password" varchar NOT NULL, + "status" varchar NOT NULL, + "trackEvents" boolean, + "newsLetter" boolean, + "imageUrl" varchar, + "title" varchar, + "externalId" varchar, + "platformId" varchar, + CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email") + ) + `) + await queryRunner.query(` + INSERT INTO "user"( + "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + ) + SELECT "id", + "created", + "updated", + "email", + "firstName", + "lastName", + "password", + "status", + "trackEvents", + "newsLetter", + "imageUrl", + "title", + "externalId", + "platformId" + FROM "temporary_user" + `) + await queryRunner.query(` + DROP TABLE "temporary_user" + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_partial_unique_email_platform_id_is_null" ON "user" ("email") + WHERE "platformId" IS NULL + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_external_id" ON "user" ("platformId", "externalId") + `) + await queryRunner.query(` + CREATE UNIQUE INDEX "idx_user_platform_id_email" ON "user" ("platformId", "email") + `) + } + +} diff --git a/packages/backend/src/app/database/postgres-connection.ts b/packages/backend/src/app/database/postgres-connection.ts index eba9524cc..a2e1b5ee4 100644 --- a/packages/backend/src/app/database/postgres-connection.ts +++ b/packages/backend/src/app/database/postgres-connection.ts @@ -95,6 +95,12 @@ import { AddEmbeddingFeatureToPlatform1701794452891 } from './migration/postgres import { AddPlatformIdToFile1701807681821 } from './migration/postgres/1701807681821-AddPlatformIdToFile' import { AddPlatformIdToFlowTemplates1703411318826 } from './migration/postgres/1703411318826-AddPlatformIdToFlowTemplates' import { RemoveFlowInstance1702379794665 } from './migration/postgres/1702379794665-remove-flow-instance' +import { RenameAppNameToPieceName1703711596105 } from './migration/postgres/1703711596105-RenameAppNameToPieceName' +import { AddVerifiedAndChangeStatus1703769034497 } from './migration/postgres/1703769034497-AddVerifiedAndChangeStatus' +import { AddAuthOptionsToPlatform1704667304953 } from './migration/postgres/1704667304953-AddAuthOptionsToPlatform' +import { AddEnableEmailAuthToPlatform1704797979825 } from './migration/postgres/1704797979825-AddEnableEmailAuthToPlatform' +import { AddGitSyncEnabledToPlatform1704636362533 } from './migration/postgres/1704636362533-AddGitSyncEnabledToPlatform' +import { AddGitRepoMigrationPostgres1704503804056 } from './migration/postgres/1704503804056-AddGitRepoMigrationPostgres' const getSslConfig = (): boolean | TlsOptions => { @@ -163,6 +169,8 @@ const getMigrations = (): (new () => MigrationInterface)[] => { AddPartialUniqueIndexForEmailAndPlatformIdIsNull1701096458822, AddPlatformIdToFile1701807681821, RemoveFlowInstance1702379794665, + RenameAppNameToPieceName1703711596105, + AddVerifiedAndChangeStatus1703769034497, ] const edition = getEdition() @@ -203,6 +211,10 @@ const getMigrations = (): (new () => MigrationInterface)[] => { AddApiKeys1701716639135, AddEmbeddingFeatureToPlatform1701794452891, AddPlatformIdToFlowTemplates1703411318826, + AddAuthOptionsToPlatform1704667304953, + AddEnableEmailAuthToPlatform1704797979825, + AddGitRepoMigrationPostgres1704503804056, + AddGitSyncEnabledToPlatform1704636362533, ) break case ApEdition.ENTERPRISE: @@ -238,6 +250,10 @@ const getMigrations = (): (new () => MigrationInterface)[] => { AddApiKeys1701716639135, AddEmbeddingFeatureToPlatform1701794452891, AddPlatformIdToFlowTemplates1703411318826, + AddAuthOptionsToPlatform1704667304953, + AddEnableEmailAuthToPlatform1704797979825, + AddGitRepoMigrationPostgres1704503804056, + AddGitSyncEnabledToPlatform1704636362533, ) break case ApEdition.COMMUNITY: diff --git a/packages/backend/src/app/database/redis-connection.ts b/packages/backend/src/app/database/redis-connection.ts index ea108f8be..a900e24cc 100755 --- a/packages/backend/src/app/database/redis-connection.ts +++ b/packages/backend/src/app/database/redis-connection.ts @@ -6,6 +6,7 @@ const url = system.get(SystemProp.REDIS_URL) const username = system.get(SystemProp.REDIS_USER) const password = system.get(SystemProp.REDIS_PASSWORD) const useSsl = system.get(SystemProp.REDIS_USE_SSL) ?? false +const db = system.getNumber(SystemProp.REDIS_DB) ?? 0 export const createRedisClient = (): Redis => { if (url) return new Redis(url) @@ -19,6 +20,7 @@ export const createRedisClient = (): Redis => { port, username, password, + db, maxRetriesPerRequest: null, tls: useSsl ? {} : undefined, }) diff --git a/packages/backend/src/app/database/seeds/dev-seeds.ts b/packages/backend/src/app/database/seeds/dev-seeds.ts index 4f4a7e466..02a0db6d8 100644 --- a/packages/backend/src/app/database/seeds/dev-seeds.ts +++ b/packages/backend/src/app/database/seeds/dev-seeds.ts @@ -1,4 +1,4 @@ -import { ApEnvironment, UserStatus } from '@activepieces/shared' +import { ApEnvironment } from '@activepieces/shared' import { authenticationService } from '../../authentication/authentication-service' import { logger } from '../../helper/logger' import { system } from '../../helper/system/system' @@ -21,7 +21,7 @@ const seedDevUser = async (): Promise => { lastName: 'lastName', trackEvents: false, newsLetter: false, - status: UserStatus.VERIFIED, + verified: true, platformId: null, }) } diff --git a/packages/backend/src/app/database/sqlite-connection.ts b/packages/backend/src/app/database/sqlite-connection.ts index 28f25a63c..3bb5a6ee1 100644 --- a/packages/backend/src/app/database/sqlite-connection.ts +++ b/packages/backend/src/app/database/sqlite-connection.ts @@ -30,7 +30,9 @@ import { AddPlatformIdToPieceMetadataSqlite1700524446967 } from './migration/sql import { AddPartialUniqueIndexForEmailAndPlatformIdIsNull1701096458822 } from './migration/common/1701096458822-add-partial-unique-index-for-email-and-platform-id-is-null' import { AddPlatformIdToFileSqlite1701808264444 } from './migration/sqlite/1701808264444-AddPlatformIdToFileSqlite' import { RemoveFlowInstanceSqlite1702412280963 } from './migration/sqlite/1702412280963-remove-flow-instance-sqlite' -import { UpdateStatusInUserSqlite1703713027818 } from './migration/1703713027818-UpdateStatusInUserSqlite' +import { UpdateStatusInUserSqlite1703713027818 } from './migration/sqlite/1703713027818-UpdateStatusInUserSqlite' +import { RenameAppNameToPieceNameSqlite1703713475755 } from './migration/sqlite/1703713475755-RenameAppNameToPieceNameSqlite' +import { AddVerifiedAndChangeStatusSqlite1703768553820 } from './migration/sqlite/1703768553820-AddVerifiedAndChangeStatusSqlite' const getSqliteDatabaseFilePath = (): string => { const apConfigDirectoryPath = system.getOrThrow(SystemProp.CONFIG_PATH) @@ -75,6 +77,8 @@ const getMigrations = (): (new () => MigrationInterface)[] => { AddPlatformIdToFileSqlite1701808264444, RemoveFlowInstanceSqlite1702412280963, UpdateStatusInUserSqlite1703713027818, + RenameAppNameToPieceNameSqlite1703713475755, + AddVerifiedAndChangeStatusSqlite1703768553820, ] const edition = getEdition() if (edition !== ApEdition.COMMUNITY) { diff --git a/packages/backend/src/app/ee/appsumo/appsumo.module.ts b/packages/backend/src/app/ee/appsumo/appsumo.module.ts index 16f8a5b93..e8caf525d 100644 --- a/packages/backend/src/app/ee/appsumo/appsumo.module.ts +++ b/packages/backend/src/app/ee/appsumo/appsumo.module.ts @@ -93,7 +93,9 @@ const appsumoController: FastifyPluginAsyncTypebox = async (fastify: FastifyInst return reply.status(StatusCodes.UNAUTHORIZED).send() } else { - const { plan_id, activation_email, action, uuid } = request.body + const { plan_id, action, uuid } = request.body + const appSumoLicense = await appsumoService.getById(uuid) + const activation_email = appSumoLicense?.activation_email ?? request.body.activation_email const appSumoPlan = appsumoService.getPlanInformation(plan_id) const user = await userService.getByPlatformAndEmail({ platformId: system.getOrThrow(SystemProp.CLOUD_PLATFORM_ID), diff --git a/packages/backend/src/app/ee/appsumo/appsumo.service.ts b/packages/backend/src/app/ee/appsumo/appsumo.service.ts index 7f1b13c30..9cb1fd9fa 100644 --- a/packages/backend/src/app/ee/appsumo/appsumo.service.ts +++ b/packages/backend/src/app/ee/appsumo/appsumo.service.ts @@ -52,6 +52,11 @@ export const appsumoService = { activation_email: email, }) }, + getById(uuid: string) { + return appsumoRepo.findOneBy({ + uuid, + }) + }, delete({ email }: { email: string }) { return appsumoRepo.delete({ activation_email: email, diff --git a/packages/backend/src/app/ee/authentication/authentication-service/hooks/authentication-helper.ts b/packages/backend/src/app/ee/authentication/authentication-service/hooks/authentication-helper.ts index 09bd4b75c..0f0f3ed0c 100644 --- a/packages/backend/src/app/ee/authentication/authentication-service/hooks/authentication-helper.ts +++ b/packages/backend/src/app/ee/authentication/authentication-service/hooks/authentication-helper.ts @@ -6,6 +6,7 @@ import { projectMemberService } from '../../../project-members/project-member.se import { projectService } from '../../../../project/project-service' import { getEdition } from '../../../../helper/secret-helper' import { userService } from '../../../../user/user-service' +import { flagService } from '../../../../flags/flag.service' async function getProjectForUserOrThrow(user: User): Promise { const invitedProject = await getProjectMemberOrThrow(user) @@ -101,17 +102,63 @@ async function assertUserIsInvitedToAnyProject({ email, platformId }: { email: s const isInvited = await isInvitedToProject({ email, platformId }) if (!isInvited) { throw new ActivepiecesError({ - code: ErrorCode.INVITATIION_ONLY_SIGN_UP, + code: ErrorCode.INVITATION_ONLY_SIGN_UP, params: {}, }) } } +async function assertEmailAuthIsEnabled({ platformId }: { platformId: string | null }): Promise { + if (isNil(platformId)) { + return + } + const platform = await platformService.getOneOrThrow(platformId) + if (!platform.ssoEnabled) { + return + } + if (!platform.emailAuthEnabled) { + throw new ActivepiecesError({ + code: ErrorCode.EMAIL_AUTH_DISABLED, + params: {}, + }) + } +} + +async function assertDomainIsAllowed({ email, platformId }: { email: string, platformId: string | null }): Promise { + if (isNil(platformId)) { + return + } + const platform = await platformService.getOneOrThrow(platformId) + if (!platform.ssoEnabled) { + return + } + const emailDomain = email.split('@')[1] + const isAllowedDomaiin = !platform.enforceAllowedAuthDomains || platform.allowedAuthDomains.includes(emailDomain) + + if (!isAllowedDomaiin) { + throw new ActivepiecesError({ + code: ErrorCode.DOMAIN_NOT_ALLOWED, + params: { + domain: emailDomain, + }, + }) + } +} + +async function assertUserIsInvitedAndDomainIsAllowed({ email, platformId }: { email: string, platformId: string | null }): Promise { + await assertDomainIsAllowed({ email, platformId }) + const customerPlatformEnabled = !isNil(platformId) && !flagService.isCloudPlatform(platformId) + if (customerPlatformEnabled) { + await assertUserIsInvitedToAnyProject({ email, platformId }) + } +} + export const authenticationHelper = { getProjectAndTokenOrThrow, autoVerifyUserIfEligible, - assertUserIsInvitedToAnyProject, - + assertUserIsInvitedAndDomainIsAllowed, + assertDomainIsAllowed, + assertEmailAuthIsEnabled, } diff --git a/packages/backend/src/app/ee/authentication/authentication-service/hooks/cloud-authentication-service-hooks.ts b/packages/backend/src/app/ee/authentication/authentication-service/hooks/cloud-authentication-service-hooks.ts index b94e8d0ee..676145a71 100644 --- a/packages/backend/src/app/ee/authentication/authentication-service/hooks/cloud-authentication-service-hooks.ts +++ b/packages/backend/src/app/ee/authentication/authentication-service/hooks/cloud-authentication-service-hooks.ts @@ -7,15 +7,17 @@ import { referralService } from '../../../referrals/referral.service' import { authenticationHelper } from './authentication-helper' import { projectService } from '../../../../project/project-service' import { userService } from '../../../../user/user-service' -import { ProjectType, UserStatus, isNil } from '@activepieces/shared' +import { ProjectType, isNil } from '@activepieces/shared' import { flagService } from '../../../../../app/flags/flag.service' export const cloudAuthenticationServiceHooks: AuthenticationServiceHooks = { + async preSignIn({ email, platformId }) { + await authenticationHelper.assertEmailAuthIsEnabled({ platformId }) + await authenticationHelper.assertDomainIsAllowed({ email, platformId }) + }, async preSignUp({ email, platformId }) { - const customerPlatformEnabled = !isNil(platformId) && !flagService.isCloudPlatform(platformId) - if (customerPlatformEnabled) { - await authenticationHelper.assertUserIsInvitedToAnyProject({ email, platformId }) - } + await authenticationHelper.assertEmailAuthIsEnabled({ platformId }) + await authenticationHelper.assertUserIsInvitedAndDomainIsAllowed({ email, platformId }) }, async postSignUp({ user, referringUserId }) { @@ -39,7 +41,7 @@ export const cloudAuthenticationServiceHooks: AuthenticationServiceHooks = { const updatedUser = await userService.getOneOrFail({ id: user.id }) const { project, token } = await authenticationHelper.getProjectAndTokenOrThrow(user) - if (updatedUser.status !== UserStatus.VERIFIED) { + if (!updatedUser.verified) { await otpService.createAndSend({ platformId: updatedUser.platformId, email: updatedUser.email, diff --git a/packages/backend/src/app/ee/authentication/authentication-service/hooks/enterprise-authentication-service-hooks.ts b/packages/backend/src/app/ee/authentication/authentication-service/hooks/enterprise-authentication-service-hooks.ts index 6785f13cf..46c6a2461 100644 --- a/packages/backend/src/app/ee/authentication/authentication-service/hooks/enterprise-authentication-service-hooks.ts +++ b/packages/backend/src/app/ee/authentication/authentication-service/hooks/enterprise-authentication-service-hooks.ts @@ -2,20 +2,23 @@ import { AuthenticationServiceHooks, } from '../../../../authentication/authentication-service/hooks/authentication-service-hooks' import { flagService } from '../../../../flags/flag.service' -import { ApFlagId, ProjectType, isNil } from '@activepieces/shared' +import { ApFlagId, ProjectType } from '@activepieces/shared' import { platformService } from '../../../platform/platform.service' import { userService } from '../../../../user/user-service' import { authenticationHelper } from './authentication-helper' import { projectService } from '../../../../project/project-service' +import { enforceLimits } from '../../../helper/license-validator' const DEFAULT_PLATFORM_NAME = 'platform' export const enterpriseAuthenticationServiceHooks: AuthenticationServiceHooks = { + async preSignIn({ email, platformId }) { + await authenticationHelper.assertEmailAuthIsEnabled({ platformId }) + await authenticationHelper.assertDomainIsAllowed({ email, platformId }) + }, async preSignUp({ email, platformId }) { - const isCustomerPlatform = !isNil(platformId) && !flagService.isCloudPlatform(platformId) - if (isCustomerPlatform) { - await authenticationHelper.assertUserIsInvitedToAnyProject({ email, platformId }) - } + await authenticationHelper.assertEmailAuthIsEnabled({ platformId }) + await authenticationHelper.assertUserIsInvitedAndDomainIsAllowed({ email, platformId }) }, async postSignUp({ user }) { const platformCreated = await flagService.getOne(ApFlagId.PLATFORM_CREATED) @@ -43,6 +46,8 @@ export const enterpriseAuthenticationServiceHooks: AuthenticationServiceHooks = await userService.updatePlatformId({ id: user.id, platformId: platform.id }) + await enforceLimits() + await flagService.save({ id: ApFlagId.PLATFORM_CREATED, value: true, @@ -67,4 +72,3 @@ export const enterpriseAuthenticationServiceHooks: AuthenticationServiceHooks = } }, } - diff --git a/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/authn-provider.ts b/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/authn-provider.ts index 400da68ec..fa64a784c 100644 --- a/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/authn-provider.ts +++ b/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/authn-provider.ts @@ -1,12 +1,11 @@ import { AuthenticationResponse } from '@activepieces/shared' -import { ThirdPartyAuthnProviderEnum } from '@activepieces/ee-shared' +import { Platform, ThirdPartyAuthnProviderEnum } from '@activepieces/ee-shared' import { googleAuthnProvider } from './google-authn-provider' import { gitHubAuthnProvider } from './github-authn-provider' export type AuthnProvider = { - getLoginUrl: () => Promise - authenticate: (platformId: string | null, authorizationCode: string) => Promise - isConfiguredByUser: () => boolean + getLoginUrl: (platform: Platform) => Promise + authenticate: (platform: Platform, authorizationCode: string) => Promise } @@ -14,8 +13,3 @@ export const providers: Record = { [ThirdPartyAuthnProviderEnum.GOOGLE]: googleAuthnProvider, [ThirdPartyAuthnProviderEnum.GITHUB]: gitHubAuthnProvider, } - -export const showThirdPartyProvidersMap: Record = { - [ThirdPartyAuthnProviderEnum.GOOGLE]: googleAuthnProvider.isConfiguredByUser(), - [ThirdPartyAuthnProviderEnum.GITHUB]: gitHubAuthnProvider.isConfiguredByUser(), -} diff --git a/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/github-authn-provider.ts b/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/github-authn-provider.ts index f251cf747..9151ffb41 100644 --- a/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/github-authn-provider.ts +++ b/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/github-authn-provider.ts @@ -1,48 +1,48 @@ -import { ActivepiecesError, AuthenticationResponse, ErrorCode, UserStatus, isNil } from '@activepieces/shared' +import { ActivepiecesError, AuthenticationResponse, ErrorCode, assertNotNullOrUndefined, isNil } from '@activepieces/shared' import { AuthnProvider } from './authn-provider' import { authenticationService } from '../../../../authentication/authentication-service' -import { system } from '../../../../helper/system/system' -import { SystemProp } from '../../../../helper/system/system-prop' import { flagService } from '../../../../flags/flag.service' +import { Platform } from '@activepieces/ee-shared' -function getClientId(): string { - return system.getOrThrow(SystemProp.FEDERATED_AUTHN_GITHUB_CLIENT_ID) -} - -function getClientSecret(): string { - return system.getOrThrow(SystemProp.FEDERATED_AUTHN_GITHUB_CLIENT_SECRET) -} +function getClientIdAndSecret(platform: Platform): { clientId: string, clientSecret: string } { + const clientInformation = platform.federatedAuthProviders.github + assertNotNullOrUndefined(clientInformation, 'Github information is not configured for this platform') + return { + clientId: clientInformation.clientId, + clientSecret: clientInformation.clientSecret, + } +} + export const gitHubAuthnProvider: AuthnProvider = { - async getLoginUrl(): Promise { + async getLoginUrl(platform: Platform): Promise { + const { clientId } = getClientIdAndSecret(platform) const loginUrl = new URL('https://github.com/login/oauth/authorize') - loginUrl.searchParams.set('client_id', getClientId()) + loginUrl.searchParams.set('client_id', clientId) loginUrl.searchParams.set('redirect_uri', flagService.getThirdPartyRedirectUrl()) loginUrl.searchParams.set('scope', 'user:email') - + return loginUrl.href }, - async authenticate(platformId, authorizationCode): Promise { - const githubAccessToken = await getGitHubAccessToken(authorizationCode) + async authenticate(platform, authorizationCode): Promise { + const { clientId, clientSecret } = getClientIdAndSecret(platform) + const githubAccessToken = await getGitHubAccessToken(clientId, clientSecret, authorizationCode) const gitHubUserInfo = await getGitHubUserInfo(githubAccessToken) - return authenticateUser(platformId, gitHubUserInfo) - }, - isConfiguredByUser(): boolean { - return !!system.get(SystemProp.FEDERATED_AUTHN_GITHUB_CLIENT_SECRET) && !!system.get(SystemProp.FEDERATED_AUTHN_GITHUB_CLIENT_ID) + return authenticateUser(platform.id, gitHubUserInfo) }, } -const getGitHubAccessToken = async (authorizationCode: string): Promise => { +const getGitHubAccessToken = async (clientId: string, clientSecret: string, authorizationCode: string): Promise => { const response = await fetch('https://github.com/login/oauth/access_token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ - client_id: getClientId(), - client_secret: getClientSecret(), + client_id: clientId, + client_secret: clientSecret, code: authorizationCode, redirect_uri: flagService.getThirdPartyRedirectUrl(), }), @@ -116,7 +116,7 @@ const getGitHubUserEmail = async (gitHubAccessToken: string): Promise => const authenticateUser = async (platformId: string | null, gitHubUserInfo: GitHubUserInfo): Promise => { return authenticationService.federatedAuthn({ email: gitHubUserInfo.email, - userStatus: UserStatus.VERIFIED, + verified: true, firstName: gitHubUserInfo.name, lastName: '', platformId, diff --git a/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/google-authn-provider.ts b/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/google-authn-provider.ts index bbe36415c..665e0e27c 100644 --- a/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/google-authn-provider.ts +++ b/packages/backend/src/app/ee/authentication/federated-authn/authn-provider/google-authn-provider.ts @@ -1,19 +1,10 @@ import jwksClient from 'jwks-rsa' -import { AuthenticationResponse, UserStatus } from '@activepieces/shared' +import { AuthenticationResponse, assertNotNullOrUndefined } from '@activepieces/shared' import { AuthnProvider } from './authn-provider' import { authenticationService } from '../../../../authentication/authentication-service' import { jwtUtils, JwtSignAlgorithm } from '../../../../helper/jwt-utils' -import { system } from '../../../../helper/system/system' -import { SystemProp } from '../../../../helper/system/system-prop' import { flagService } from '../../../../flags/flag.service' - -function getClientId(): string { - return system.getOrThrow(SystemProp.FEDERATED_AUTHN_GOOGLE_CLIENT_ID) -} - -function getClientSecret(): string { - return system.getOrThrow(SystemProp.FEDERATED_AUTHN_GOOGLE_CLIENT_SECRET) -} +import { Platform } from '@activepieces/ee-shared' const JWKS_URI = 'https://www.googleapis.com/oauth2/v3/certs' @@ -23,10 +14,20 @@ const keyLoader = jwksClient({ jwksUri: JWKS_URI, }) +function getClientIdAndSecret(platform: Platform): { clientId: string, clientSecret: string } { + const clientInformation = platform.federatedAuthProviders.google + assertNotNullOrUndefined(clientInformation, 'Google information is not configured for this platform') + return { + clientId: clientInformation.clientId, + clientSecret: clientInformation.clientSecret, + } +} + export const googleAuthnProvider: AuthnProvider = { - async getLoginUrl(): Promise { + async getLoginUrl(platform: Platform): Promise { + const { clientId } = getClientIdAndSecret(platform) const loginUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth') - loginUrl.searchParams.set('client_id', getClientId()) + loginUrl.searchParams.set('client_id', clientId) loginUrl.searchParams.set('redirect_uri', flagService.getThirdPartyRedirectUrl()) loginUrl.searchParams.set('scope', 'email profile') loginUrl.searchParams.set('response_type', 'code') @@ -34,17 +35,15 @@ export const googleAuthnProvider: AuthnProvider = { return loginUrl.href }, - async authenticate(platformId, authorizationCode): Promise { - const idToken = await exchangeCodeForIdToken(authorizationCode) - const idTokenPayload = await verifyIdToken(idToken) - return generateAuthenticationResponse(platformId, idTokenPayload) - }, - isConfiguredByUser(): boolean { - return !!system.get(SystemProp.FEDERATED_AUTHN_GOOGLE_CLIENT_SECRET) && !!system.get(SystemProp.FEDERATED_AUTHN_GOOGLE_CLIENT_ID) + async authenticate(platform, authorizationCode): Promise { + const { clientId, clientSecret } = getClientIdAndSecret(platform) + const idToken = await exchangeCodeForIdToken(clientId, clientSecret, authorizationCode) + const idTokenPayload = await verifyIdToken(clientId, idToken) + return generateAuthenticationResponse(platform.id, idTokenPayload) }, } -const exchangeCodeForIdToken = async (code: string): Promise => { +const exchangeCodeForIdToken = async (clientId: string, clientSecret: string, code: string): Promise => { const response = await fetch('https://oauth2.googleapis.com/token', { method: 'POST', headers: { @@ -52,8 +51,8 @@ const exchangeCodeForIdToken = async (code: string): Promise => { }, body: new URLSearchParams({ code, - client_id: getClientId(), - client_secret: getClientSecret(), + client_id: clientId, + client_secret: clientSecret, redirect_uri: flagService.getThirdPartyRedirectUrl(), grant_type: 'authorization_code', }), @@ -63,7 +62,7 @@ const exchangeCodeForIdToken = async (code: string): Promise => { return idToken } -const verifyIdToken = async (idToken: string): Promise => { +const verifyIdToken = async (clientId: string, idToken: string): Promise => { const { header } = jwtUtils.decode({ jwt: idToken }) const signingKey = await keyLoader.getSigningKey(header.kid) const publicKey = signingKey.getPublicKey() @@ -73,7 +72,7 @@ const verifyIdToken = async (idToken: string): Promise => { key: publicKey, issuer: ['accounts.google.com', 'https://accounts.google.com'], algorithm: JwtSignAlgorithm.RS256, - audience: getClientId(), + audience: clientId, }) return { @@ -87,7 +86,7 @@ const verifyIdToken = async (idToken: string): Promise => { const generateAuthenticationResponse = async (platformId: string | null, idTokenPayload: IdTokenPayload): Promise => { return authenticationService.federatedAuthn({ email: idTokenPayload.email, - userStatus: UserStatus.VERIFIED, + verified: true, firstName: idTokenPayload.givenName, lastName: idTokenPayload.familyName, platformId, diff --git a/packages/backend/src/app/ee/authentication/federated-authn/federated-authn-controller.ts b/packages/backend/src/app/ee/authentication/federated-authn/federated-authn-controller.ts index 04fc1153b..4626c52cd 100644 --- a/packages/backend/src/app/ee/authentication/federated-authn/federated-authn-controller.ts +++ b/packages/backend/src/app/ee/authentication/federated-authn/federated-authn-controller.ts @@ -1,18 +1,22 @@ import { FastifyPluginAsyncTypebox, Type } from '@fastify/type-provider-typebox' import { federatedAuthnService } from './federated-authn-service' import { ClaimTokenRequest, ThirdPartyAuthnProviderEnum } from '@activepieces/ee-shared' -import { ALL_PRINICPAL_TYPES } from '@activepieces/shared' +import { ALL_PRINICPAL_TYPES, assertNotNullOrUndefined } from '@activepieces/shared' import { resolvePlatformIdForRequest } from '../../platform/lib/platform-utils' export const federatedAuthnController: FastifyPluginAsyncTypebox = async (app) => { app.get('/login', LoginRequestSchema, async (req) => { + const platformId = await resolvePlatformIdForRequest(req) + assertNotNullOrUndefined(platformId, 'Platform id is not defined') return federatedAuthnService.login({ providerName: req.query.providerName, + platformId, }) }) app.post('/claim', ClaimTokenRequestSchema, async (req) => { const platformId = await resolvePlatformIdForRequest(req) + assertNotNullOrUndefined(platformId, 'Platform id is not defined') return federatedAuthnService.claim({ platformId, providerName: req.body.providerName, diff --git a/packages/backend/src/app/ee/authentication/federated-authn/federated-authn-service.ts b/packages/backend/src/app/ee/authentication/federated-authn/federated-authn-service.ts index f57fbef00..2c823df8a 100644 --- a/packages/backend/src/app/ee/authentication/federated-authn/federated-authn-service.ts +++ b/packages/backend/src/app/ee/authentication/federated-authn/federated-authn-service.ts @@ -1,12 +1,14 @@ import { AuthenticationResponse } from '@activepieces/shared' import { FederatedAuthnLoginResponse, ThirdPartyAuthnProviderEnum } from '@activepieces/ee-shared' import { providers } from './authn-provider/authn-provider' +import { platformService } from '../../platform/platform.service' export const federatedAuthnService = { - async login({ providerName }: LoginParams): Promise { + async login({ providerName, platformId }: LoginParams): Promise { const provider = providers[providerName] - const loginUrl = await provider.getLoginUrl() + const platform = await platformService.getOneOrThrow(platformId) + const loginUrl = await provider.getLoginUrl(platform) return { loginUrl, @@ -15,17 +17,19 @@ export const federatedAuthnService = { async claim({ platformId, providerName, code }: ClaimParams): Promise { const provider = providers[providerName] - return provider.authenticate(platformId, code) + const platform = await platformService.getOneOrThrow(platformId) + return provider.authenticate(platform, code) }, } type LoginParams = { + platformId: string providerName: ThirdPartyAuthnProviderEnum } type ClaimParams = { - platformId: string | null + platformId: string providerName: ThirdPartyAuthnProviderEnum code: string } diff --git a/packages/backend/src/app/ee/connection-keys/connection-key.service.ts b/packages/backend/src/app/ee/connection-keys/connection-key.service.ts index 06360a8b5..f70bf3b70 100644 --- a/packages/backend/src/app/ee/connection-keys/connection-key.service.ts +++ b/packages/backend/src/app/ee/connection-keys/connection-key.service.ts @@ -75,7 +75,7 @@ export const connectionKeyService = { request: { projectId, name: `${appCredential.appName}_${connectionName}`, - appName: finalAppName, + pieceName: finalAppName, type: AppConnectionType.SECRET_TEXT, value: { type: AppConnectionType.SECRET_TEXT, @@ -90,7 +90,7 @@ export const connectionKeyService = { projectId, request: { name: `${appCredential.appName}_${connectionName}`, - appName: finalAppName, + pieceName: finalAppName, projectId, type: AppConnectionType.OAUTH2, value: { diff --git a/packages/backend/src/app/ee/flags/enterprise-flags.hooks.ts b/packages/backend/src/app/ee/flags/enterprise-flags.hooks.ts index 73c2563e0..15f633c9d 100644 --- a/packages/backend/src/app/ee/flags/enterprise-flags.hooks.ts +++ b/packages/backend/src/app/ee/flags/enterprise-flags.hooks.ts @@ -11,22 +11,27 @@ export const enterpriseFlagsHooks: FlagsServiceHooks = { const modifiedFlags = { ...flags } const hostname = request.hostname const platformId = await resolvePlatformIdForRequest(request) - const isCustomerPlatform = !isNil(platformId) && !flagService.isCloudPlatform(platformId) + if (isNil(platformId)) { + return modifiedFlags + } + const platform = await platformService.getOneOrThrow(platformId) + modifiedFlags[ApFlagId.THIRD_PARTY_AUTH_PROVIDERS_TO_SHOW_MAP] = { + [ThirdPartyAuthnProviderEnum.GOOGLE]: !isNil(platform.federatedAuthProviders.google), + [ThirdPartyAuthnProviderEnum.GITHUB]: !isNil(platform.federatedAuthProviders.github), + } + modifiedFlags[ApFlagId.EMAIL_AUTH_ENABLED] = platform.emailAuthEnabled + const isCustomerPlatform = !flagService.isCloudPlatform(platformId) if (isCustomerPlatform ) { - const platform = await platformService.getOneOrThrow(platformId) modifiedFlags[ApFlagId.THEME] = await apperanceHelper.getTheme({ platformId }) modifiedFlags[ApFlagId.SHOW_COMMUNITY] = false modifiedFlags[ApFlagId.SHOW_DOCS] = false modifiedFlags[ApFlagId.SHOW_BILLING] = false - modifiedFlags[ApFlagId.THIRD_PARTY_AUTH_PROVIDERS_TO_SHOW_MAP] = { - [ThirdPartyAuthnProviderEnum.GOOGLE]: false, - [ThirdPartyAuthnProviderEnum.GITHUB]: false, - }, modifiedFlags[ApFlagId.SHOW_BLOG_GUIDE] = false modifiedFlags[ApFlagId.SHOW_COMMUNITY_PIECES] = false modifiedFlags[ApFlagId.SHOW_SIGN_UP_LINK] = false modifiedFlags[ApFlagId.SHOW_POWERED_BY_AP] = platform.showPoweredBy modifiedFlags[ApFlagId.CLOUD_AUTH_ENABLED] = platform.cloudAuthEnabled + modifiedFlags[ApFlagId.SHOW_GIT_SYNC] = platform.gitSyncEnabled modifiedFlags[ApFlagId.FRONTEND_URL] = `https://${hostname}` modifiedFlags[ApFlagId.WEBHOOK_URL_PREFIX] = `https://${hostname}/api/v1/webhooks` modifiedFlags[ApFlagId.THIRD_PARTY_AUTH_PROVIDER_REDIRECT_URL] = `https://${hostname}/redirect` diff --git a/packages/backend/src/app/ee/flow-template/flow-template.entity.ts b/packages/backend/src/app/ee/flow-template/flow-template.entity.ts index b6f1af7cf..5adc69d2d 100644 --- a/packages/backend/src/app/ee/flow-template/flow-template.entity.ts +++ b/packages/backend/src/app/ee/flow-template/flow-template.entity.ts @@ -24,7 +24,7 @@ export const FlowTemplateEntity = new EntitySchema({ }, platformId: { type: String, - nullable: true, + nullable: false, }, projectId: { type: String, diff --git a/packages/backend/src/app/ee/flow-template/flow-template.service.ts b/packages/backend/src/app/ee/flow-template/flow-template.service.ts index e3ecd653c..fefb58bba 100644 --- a/packages/backend/src/app/ee/flow-template/flow-template.service.ts +++ b/packages/backend/src/app/ee/flow-template/flow-template.service.ts @@ -8,10 +8,12 @@ import { paginationHelper } from '../../helper/pagination/pagination-utils' const templateRepo = databaseConnection.getRepository(FlowTemplateEntity) export const flowTemplateService = { - upsert: async (platformId: string | undefined, projectId: string | undefined, { description, type, template, blogUrl, tags, id }: CreateFlowTemplateRequest): Promise => { + upsert: async (platformId: string | undefined, projectId: string | undefined, { description, type, template, blogUrl, tags, id }: CreateFlowTemplateRequest): Promise => { const flowTemplate: FlowVersionTemplate = template + const newTags = tags ?? [] + const newId = id ?? apId() await templateRepo.upsert({ - id: id ?? apId(), + id: newId, // eslint-disable-next-line @typescript-eslint/no-explicit-any template: flowTemplate as any, name: flowTemplate.displayName, @@ -19,14 +21,14 @@ export const flowTemplateService = { pieces: flowHelper.getUsedPieces(flowTemplate.trigger), blogUrl, type, - tags, + tags: newTags, created: new Date().toISOString(), updated: new Date().toISOString(), platformId, projectId, }, ['id']) return templateRepo.findOneByOrFail({ - id, + id: newId, }) }, list: async (platformId: string, { pieces, tags, search }: ListFlowTemplatesRequest): Promise> => { diff --git a/packages/backend/src/app/ee/git-repos/git-repo.controller.ts b/packages/backend/src/app/ee/git-repos/git-repo.controller.ts new file mode 100644 index 000000000..9144c632b --- /dev/null +++ b/packages/backend/src/app/ee/git-repos/git-repo.controller.ts @@ -0,0 +1,118 @@ +import { FastifyPluginCallbackTypebox, Type } from '@fastify/type-provider-typebox' +import { gitRepoService } from './git-repo.service' +import { PrincipalType, SeekPage } from '@activepieces/shared' +import { ConfigureRepoRequest, GitRepoWithoutSenestiveData, PushGitRepoRequest } from '@activepieces/ee-shared' +import { StatusCodes } from 'http-status-codes' + + +export const gitRepoController: FastifyPluginCallbackTypebox = (app, _options, done): void => { + + app.post('/', ConfigureRepoRequestSchema, async (request, reply) => { + await reply.status(StatusCodes.CREATED).send(await gitRepoService.upsert(request.body)) + }) + + app.get('/', ListRepoRequestSchema, async (request) => { + return gitRepoService.list(request.query) + }) + + app.post('/:id/push', PushRepoRequestSchema, async (request) => { + await gitRepoService.push({ + id: request.params.id, + commitMessage: request.body.commitMessage, + }) + }) + + app.post('/:id/pull', PullRepoRequestSchema, async (request) => { + await gitRepoService.pull({ + id: request.params.id, + }) + }) + + app.delete('/:id', DeleteRepoRequestSchema, async (request, reply) => { + await gitRepoService.delete({ + id: request.params.id, + projectId: request.principal.projectId, + }) + await reply.status(StatusCodes.NO_CONTENT).send() + }) + + done() +} + +const DeleteRepoRequestSchema = { + config: { + allowedPrincipals: [PrincipalType.SERVICE, PrincipalType.USER], + }, + schema: { + tags: ['git-repo'], + description: 'Delete a git repository information for a project.', + params: Type.Object({ + id: Type.String(), + }), + response: { + [StatusCodes.NO_CONTENT]: Type.Undefined(), + }, + }, +} + +const PullRepoRequestSchema = { + config: { + allowedPrincipals: [PrincipalType.SERVICE, PrincipalType.USER], + }, + schema: { + tags: ['git-repo'], + description: 'Pull all changes from the git repository and overwrite any conflicting changes in the project.', + params: Type.Object({ + id: Type.String(), + }), + response: { + [StatusCodes.NO_CONTENT]: Type.Undefined(), + }, + }, +} + +const PushRepoRequestSchema = { + config: { + allowedPrincipals: [PrincipalType.SERVICE, PrincipalType.USER], + }, + schema: { + tags: ['git-repo'], + description: 'Push all changes from the project and overwrite any conflicting changes in the git repository.', + body: PushGitRepoRequest, + params: Type.Object({ + id: Type.String(), + }), + response: { + [StatusCodes.NO_CONTENT]: Type.Undefined(), + }, + }, +} + +const ConfigureRepoRequestSchema = { + config: { + allowedPrincipals: [PrincipalType.SERVICE, PrincipalType.USER], + }, + schema: { + tags: ['git-repo'], + description: 'Upsert a git repository information for a project.', + body: ConfigureRepoRequest, + response: { + [StatusCodes.CREATED]: GitRepoWithoutSenestiveData, + }, + }, +} + +const ListRepoRequestSchema = { + config: { + allowedPrincipals: [PrincipalType.SERVICE, PrincipalType.USER], + }, + schema: { + tags: ['git-repo'], + querystring: Type.Object({ + projectId: Type.String(), + }), + response: { + [StatusCodes.OK]: SeekPage(GitRepoWithoutSenestiveData), + }, + }, +} \ No newline at end of file diff --git a/packages/backend/src/app/ee/git-repos/git-repo.entity.ts b/packages/backend/src/app/ee/git-repos/git-repo.entity.ts new file mode 100644 index 000000000..a3dbfdea3 --- /dev/null +++ b/packages/backend/src/app/ee/git-repos/git-repo.entity.ts @@ -0,0 +1,49 @@ +import { EntitySchema } from 'typeorm' +import { GitRepo } from '@activepieces/ee-shared' +import { ApIdSchema, BaseColumnSchemaPart } from '../../database/database-common' +import { Project } from '@activepieces/shared' + + +type GitRepoSchema = GitRepo & { + project: Project +} + +export const GitRepoEntity = new EntitySchema({ + name: 'git_repo', + columns: { + ...BaseColumnSchemaPart, + projectId: ApIdSchema, + remoteUrl: { + type: String, + nullable: false, + }, + branch: { + type: String, + nullable: false, + }, + sshPrivateKey: { + type: String, + nullable: true, + }, + }, + indices: [ + { + name: 'idx_git_repo_project_id', + columns: ['projectId'], + unique: true, + }, + ], + relations: { + project: { + type: 'one-to-one', + target: 'project', + cascade: true, + onDelete: 'CASCADE', + joinColumn: { + name: 'projectId', + referencedColumnName: 'id', + foreignKeyConstraintName: 'fk_git_repo_project_id', + }, + }, + }, +}) diff --git a/packages/backend/src/app/ee/git-repos/git-repo.module.ts b/packages/backend/src/app/ee/git-repos/git-repo.module.ts new file mode 100644 index 000000000..c182c508f --- /dev/null +++ b/packages/backend/src/app/ee/git-repos/git-repo.module.ts @@ -0,0 +1,7 @@ +import { FastifyPluginAsync } from 'fastify' +import { gitRepoController } from './git-repo.controller' + +export const gitRepoModule: FastifyPluginAsync = async (app) => { + await app.register(gitRepoController, { prefix: '/v1/git-repos' }) +} + diff --git a/packages/backend/src/app/ee/git-repos/git-repo.service.ts b/packages/backend/src/app/ee/git-repos/git-repo.service.ts new file mode 100644 index 000000000..279bcfcfd --- /dev/null +++ b/packages/backend/src/app/ee/git-repos/git-repo.service.ts @@ -0,0 +1,165 @@ +import { SimpleGit, simpleGit } from 'simple-git' +import fs from 'fs/promises' +import path from 'path' +import { databaseConnection } from '../../database/database-connection' +import { GitRepoEntity } from './git-repo.entity' +import { ConfigureRepoRequest, GitRepo } from '@activepieces/ee-shared' +import { ActivepiecesError, apId, ErrorCode, isNil, SeekPage } from '@activepieces/shared' +import { paginationHelper } from '../../helper/pagination/pagination-utils' +import { FlowSyncOperation, gitSyncHelper } from './git-sync-helper' + +const repo = databaseConnection.getRepository(GitRepoEntity) + +export const gitRepoService = { + async upsert({ projectId, sshPrivateKey, branch, remoteUrl }: ConfigureRepoRequest): Promise { + const existingRepo = await repo.findOneBy({ projectId }) + const id = existingRepo?.id ?? apId() + await repo.upsert({ + id, + projectId, + sshPrivateKey, + branch, + remoteUrl, + }, ['projectId']) + return repo.findOneByOrFail({ id }) + }, + async getOrThrow({ id }: { id: string }): Promise { + const gitRepo = await repo.findOneByOrFail({ id }) + if (isNil(gitRepo)) { + throw new ActivepiecesError({ + code: ErrorCode.ENTITY_NOT_FOUND, + params: { + entityId: id, + entityType: 'git-repo', + }, + }) + } + return gitRepo + }, + async list({ projectId }: { projectId: string }): Promise> { + const repos = await repo.findBy({ projectId }) + return paginationHelper.createPage(repos, null) + }, + async push({ id, commitMessage }: PushGitRepoRequest): Promise { + const gitRepo = await gitRepoService.getOrThrow({ id }) + const { flowFolderPath, git } = await createGitRepoAndReturnPaths(gitRepo) + const operations = await planPushOperations(gitRepo.projectId, flowFolderPath) + await gitSyncHelper.applyFlowOperations({ projectId: gitRepo.projectId, flowFolderPath, operations }) + await commitAndPush(git, gitRepo, commitMessage) + + }, + async pull({ id }: PullGitRepoRequest): Promise { + const gitRepo = await gitRepoService.getOrThrow({ id }) + const { flowFolderPath } = await createGitRepoAndReturnPaths(gitRepo) + const operations: FlowSyncOperation[] = await planPullOperations(gitRepo.projectId, flowFolderPath) + await gitSyncHelper.applyFlowOperations({ projectId: gitRepo.projectId, flowFolderPath, operations }) + }, + async delete({ id, projectId }: { id: string, projectId: string }): Promise { + const gitRepo = await repo.findOneBy({ id, projectId }) + if (isNil(gitRepo)) { + throw new ActivepiecesError({ + code: ErrorCode.ENTITY_NOT_FOUND, + params: { + entityId: id, + entityType: 'git-repo', + }, + }) + } + await repo.delete({ id, projectId }) + }, +} + +async function planPullOperations(projectId: string, flowPath: string): Promise { + const projectFlows = await gitSyncHelper.fetchFlowsForProject(projectId) + const gitFlows = await gitSyncHelper.parseFlowsFromDirectory(flowPath) + const deleteOperations: FlowSyncOperation[] = projectFlows.filter(f => gitFlows.findIndex(pf => pf.id === f.id) === -1).map(flow => { + return { + type: 'delete_flow_from_project', + flowId: flow.id, + } + }) + const upsertOperations: FlowSyncOperation[] = gitFlows.map(flow => { + return { + type: 'upsert_flow_into_project', + flow, + } + }) + return [...deleteOperations, ...upsertOperations] +} + + +async function planPushOperations(projectId: string, flowPath: string): Promise { + const projectFlows = await gitSyncHelper.fetchFlowsForProject(projectId) + const gitFlows = await gitSyncHelper.parseFlowsFromDirectory(flowPath) + const deleteOperations: FlowSyncOperation[] = gitFlows.filter(f => projectFlows.findIndex(pf => pf.id === f.id) === -1).map(flow => { + return { + type: 'delete_flow_from_git', + flowId: flow.id, + } + }) + const upsertOperations: FlowSyncOperation[] = projectFlows.map(flow => { + return { + type: 'upsert_flow_into_git', + flow, + } + }) + return [...deleteOperations, ...upsertOperations] +} + +async function createGitRepoAndReturnPaths(gitRepo: GitRepo): Promise<{ flowFolderPath: string, git: SimpleGit }> { + const tmpFolder = path.join('/', 'tmp', 'repo', gitRepo.projectId) + try { + await fs.rmdir(tmpFolder, { recursive: true }) + } + catch (e) { + // ignore + } + const flowFolderPath = path.join(tmpFolder, 'flows') + await fs.mkdir(flowFolderPath, { recursive: true }) + const git = await initGitRepo(gitRepo, tmpFolder) + + return { + git, + flowFolderPath, + } +} + + +async function createOrGetSshKeyPath(gitRepo: GitRepo): Promise { + const keyPath = path.resolve(path.join('tmp', 'keys', gitRepo.id)) + await fs.mkdir(path.dirname(keyPath), { recursive: true }) + await fs.writeFile(keyPath, gitRepo.sshPrivateKey) + await fs.chmod(keyPath, 0o600) + return keyPath +} + +async function initGitRepo(gitRepo: GitRepo, baseDir: string): Promise { + const keyPath = await createOrGetSshKeyPath(gitRepo) + const git = simpleGit({ + baseDir, + binary: 'git', + }).env('GIT_SSH_COMMAND', `ssh -i ${keyPath} -o StrictHostKeyChecking=no`) + await git.init() + await git.addRemote('origin', gitRepo.remoteUrl) + await git.branch(['-M', gitRepo.branch]) + await git.pull('origin', gitRepo.branch) + await git.addConfig('user.email', 'noreply@activepieces.com') + await git.addConfig('user.name', 'Activepieces') + return git +} + +async function commitAndPush(git: SimpleGit, gitRepo: GitRepo, commitMessage: string): Promise { + await git.add('.') + await git.commit(commitMessage) + await git.push('origin', gitRepo.branch) +} + + +type PushGitRepoRequest = { + id: string + commitMessage: string +} + +type PullGitRepoRequest = { + id: string +} diff --git a/packages/backend/src/app/ee/git-repos/git-sync-helper.ts b/packages/backend/src/app/ee/git-repos/git-sync-helper.ts new file mode 100644 index 000000000..35ccfeb90 --- /dev/null +++ b/packages/backend/src/app/ee/git-repos/git-sync-helper.ts @@ -0,0 +1,129 @@ +import { Flow, FlowOperationType, PopulatedFlow } from '@activepieces/shared' +import { flowService } from '../../flows/flow/flow.service' +import fs from 'fs/promises' +import path from 'path' +import { projectService } from '../../project/project-service' +import { flowRepo } from '../../flows/flow/flow.repo' + +type DeleteFlowFromGitOperation = { + type: 'delete_flow_from_git' + flowId: string +} + +type DeleteFlowFromProjectOperation = { + type: 'delete_flow_from_project' + flowId: string +} + +type UpsertFlowIntoProjectOperation = { + type: 'upsert_flow_into_project' + flow: PopulatedFlow +} + +type UpsertFlowOperation = { + type: 'upsert_flow_into_git' + flow: PopulatedFlow +} + +export type FlowSyncOperation = DeleteFlowFromGitOperation | UpsertFlowIntoProjectOperation | DeleteFlowFromProjectOperation | UpsertFlowOperation + +async function fetchFlowsForProject(projectId: string): Promise { + const flows = await flowRepo.findBy({ + projectId, + }) + return Promise.all(flows.map(f => { + return flowService.getOnePopulatedOrThrow({ + id: f.id, + projectId, + removeSecrets: false, + }) + })) +} + +async function parseFlowsFromDirectory(flowPath: string): Promise { + const flowFiles = await fs.readdir(flowPath) + const parsedFlows = [] + for (const file of flowFiles) { + const flow: PopulatedFlow = JSON.parse(await fs.readFile(path.join(flowPath, file), 'utf-8')) + parsedFlows.push(flow) + } + return parsedFlows +} + +async function applyFlowOperations({ projectId, flowFolderPath, operations }: { projectId: string, flowFolderPath: string, operations: FlowSyncOperation[] }): Promise { + for (const operation of operations) { + switch (operation.type) { + case 'upsert_flow_into_git': + { + await upsertFlowToGit(operation.flow, flowFolderPath) + break + } + case 'delete_flow_from_git': + { + await deleteFlowFromGit(operation.flowId, flowFolderPath) + break + } + case 'upsert_flow_into_project': + { + await upsertFlowToProject(operation.flow, projectId) + break + } + case 'delete_flow_from_project': + { + await deleteFlowFromProject(operation.flowId, projectId) + break + } + } + } +} + +async function upsertFlowToProject(flow: PopulatedFlow, projectId: string): Promise { + const existingFlow = await flowService.getOne({ id: flow.id, projectId }) + let flowId = flow.id + if (!existingFlow) { + const newFlow = await flowService.create({ + projectId, + request: { + displayName: flow.version.displayName, + folderId: flow.folderId ?? undefined, + projectId, + }, + }) + flowId = newFlow.id + } + const project = await projectService.getOneOrThrow(projectId) + await flowService.update({ + id: flowId, + projectId, + lock: true, + userId: project.ownerId, + operation: { + type: FlowOperationType.IMPORT_FLOW, + request: { + displayName: flow.version.displayName, + trigger: flow.version.trigger, + }, + }, + }) + +} + +async function upsertFlowToGit(flow: Flow, flowFolderPath: string): Promise { + const flowJsonPath = path.join(flowFolderPath, `${flow.id}.json`) + await fs.writeFile(flowJsonPath, JSON.stringify(flow, null, 2)) +} + +async function deleteFlowFromGit(flowId: string, flowFolderPath: string): Promise { + const flowJsonPath = path.join(flowFolderPath, `${flowId}.json`) + await fs.unlink(flowJsonPath) +} + +async function deleteFlowFromProject(flowId: string, projectId: string): Promise { + await flowService.delete({ id: flowId, projectId }) +} + +export const gitSyncHelper = { + fetchFlowsForProject, + parseFlowsFromDirectory, + applyFlowOperations, +} diff --git a/packages/backend/src/app/ee/helper/email/email-service.ts b/packages/backend/src/app/ee/helper/email/email-service.ts index 962104283..d5dc92572 100644 --- a/packages/backend/src/app/ee/helper/email/email-service.ts +++ b/packages/backend/src/app/ee/helper/email/email-service.ts @@ -1,5 +1,5 @@ import { getEdition } from '../../../helper/secret-helper' -import { ApEdition, User, UserStatus, assertNotNullOrUndefined, isNil } from '@activepieces/shared' +import { ApEdition, User, assertNotNullOrUndefined, isNil } from '@activepieces/shared' import fs from 'node:fs/promises' import Mustache from 'mustache' import nodemailer from 'nodemailer' @@ -25,8 +25,7 @@ export const emailService = { return } - const project = await projectService.getOne(projectId) - assertNotNullOrUndefined(project, 'project') + const project = await projectService.getOneOrThrow(projectId) const memberToken: ProjectMemberToken = { id: invitationId, } @@ -82,7 +81,7 @@ export const emailService = { if (![ApEdition.CLOUD, ApEdition.ENTERPRISE].includes(edition)) { return } - if (user.status === UserStatus.VERIFIED && type === OtpType.EMAIL_VERIFICATION) { + if (user.verified && type === OtpType.EMAIL_VERIFICATION) { return } logger.info('Sending OTP email', { email: user.email, otp, userId: user.id, firstName: user.email, type }) diff --git a/packages/backend/src/app/ee/helper/license-validator/index.ts b/packages/backend/src/app/ee/helper/license-validator/index.ts index f27d8133f..cbb14e60e 100644 --- a/packages/backend/src/app/ee/helper/license-validator/index.ts +++ b/packages/backend/src/app/ee/helper/license-validator/index.ts @@ -1,9 +1,12 @@ import { ApEnvironment } from '@activepieces/shared' -import { LicenseValidator } from './license-validator' +import { LiceneseStatus, LicenseValidator } from './license-validator' import { noOpLicenseValidator } from './no-op-license-validator' import { networkLicenseValidator } from './network-license-validator' +import { ApEdition } from '@activepieces/shared' import { system } from '../../../helper/system/system' import { SystemProp } from '../../../helper/system/system-prop' +import { platformService } from '../../platform/platform.service' +import { logger } from '../../../helper/logger' const variant: Record = { [ApEnvironment.PRODUCTION]: networkLicenseValidator, @@ -13,4 +16,39 @@ const variant: Record = { const env = system.getOrThrow(SystemProp.ENVIRONMENT) -export const licenseValidator = variant[env] +const licenseValidator = variant[env] + +export async function enforceLimits(): Promise { + const edition = system.getOrThrow(SystemProp.EDITION) + if (edition !== ApEdition.ENTERPRISE) { + return + } + const license = await licenseValidator.validate() + switch (license.status) { + case LiceneseStatus.VALID:{ + const oldestPlatform = await platformService.getOldestPlatform() + if (!oldestPlatform) { + break + } + await platformService.update({ + id: oldestPlatform.id, + userId: oldestPlatform.ownerId, + showPoweredBy: license.showPoweredBy, + embeddingEnabled: license.embeddingEnabled, + ssoEnabled: license.ssoEnabled, + gitSyncEnabled: license.gitSyncEnabled, + }) + break + } + case LiceneseStatus.INVALID: { + logger.error('[ERROR]: License key is not valid. Please contact sales@activepieces.com') + process.exit(1) + break + } + case LiceneseStatus.UNKNOWN:{ + // We don't want to block the application from starting if the license is unknown + // TODO find a better way to handle this + break + } + } +} diff --git a/packages/backend/src/app/ee/helper/license-validator/license-validator.ts b/packages/backend/src/app/ee/helper/license-validator/license-validator.ts index 0f6c2bbb2..ed4845693 100644 --- a/packages/backend/src/app/ee/helper/license-validator/license-validator.ts +++ b/packages/backend/src/app/ee/helper/license-validator/license-validator.ts @@ -1,3 +1,23 @@ + +export enum LiceneseStatus { + VALID = 'VALID', + INVALID = 'INVALID', + UNKNOWN = 'UNKNOWN', +} + +export type SuccessLicenseResponse = { + status: LiceneseStatus.VALID + showPoweredBy?: boolean + ssoEnabled?: boolean + embeddingEnabled?: boolean + gitSyncEnabled?: boolean +} +export type LicenseResponse = SuccessLicenseResponse | { + status: LiceneseStatus.INVALID +} | { + status: LiceneseStatus.UNKNOWN +} + export type LicenseValidator = { - validate: () => Promise + validate: () => Promise } diff --git a/packages/backend/src/app/ee/helper/license-validator/network-license-validator.ts b/packages/backend/src/app/ee/helper/license-validator/network-license-validator.ts index 1f3a31419..ae618389c 100644 --- a/packages/backend/src/app/ee/helper/license-validator/network-license-validator.ts +++ b/packages/backend/src/app/ee/helper/license-validator/network-license-validator.ts @@ -1,6 +1,6 @@ import axios from 'axios' import { logger } from '../../../helper/logger' -import { LicenseValidator } from './license-validator' +import { LiceneseStatus, LicenseValidator } from './license-validator' import { system } from '../../../helper/system/system' import { SystemProp } from '../../../helper/system/system-prop' @@ -10,12 +10,24 @@ export const networkLicenseValidator: LicenseValidator = { try { const res = await axios.post('https://secrets.activepieces.com/verify', { licenseKey: license }) logger.debug({ name: 'NetworkLicenseValidator#validate', response: res.data }) - return res.status === 200 + if (res.status !== 200) { + return { + status: LiceneseStatus.INVALID, + } + } + return { + status: LiceneseStatus.VALID, + showPoweredBy: res.data.showPoweredBy, + embeddingEnabled: res.data.embeddingEnabled, + gitSyncEnabled: res.data.gitSyncEnabled, + ssoEnabled: res.data.ssoEnabled, + } } catch (err) { logger.error({ name: 'NetworkLicenseValidator#validate', err }) - // TODO FIX - return true + return { + status: LiceneseStatus.UNKNOWN, + } } }, } diff --git a/packages/backend/src/app/ee/helper/license-validator/no-op-license-validator.ts b/packages/backend/src/app/ee/helper/license-validator/no-op-license-validator.ts index 978b9c0ec..6e01966c7 100644 --- a/packages/backend/src/app/ee/helper/license-validator/no-op-license-validator.ts +++ b/packages/backend/src/app/ee/helper/license-validator/no-op-license-validator.ts @@ -1,7 +1,9 @@ -import { LicenseValidator } from './license-validator' +import { LiceneseStatus, LicenseValidator } from './license-validator' export const noOpLicenseValidator: LicenseValidator = { async validate() { - return true + return { + status: LiceneseStatus.VALID, + } }, } diff --git a/packages/backend/src/app/ee/managed-authn/managed-authn-service.ts b/packages/backend/src/app/ee/managed-authn/managed-authn-service.ts index dd133bc96..e318d592d 100644 --- a/packages/backend/src/app/ee/managed-authn/managed-authn-service.ts +++ b/packages/backend/src/app/ee/managed-authn/managed-authn-service.ts @@ -1,6 +1,6 @@ import { randomBytes as randomBytesCallback } from 'node:crypto' import { promisify } from 'node:util' -import { AuthenticationResponse, PlatformRole, PrincipalType, Project, ProjectId, ProjectType, User, UserStatus } from '@activepieces/shared' +import { AuthenticationResponse, PlatformRole, PrincipalType, Project, ProjectId, ProjectType, User } from '@activepieces/shared' import { userService } from '../../user/user-service' import { PlatformId, ProjectMemberRole, ProjectMemberStatus } from '@activepieces/ee-shared' import { platformService } from '../platform/platform.service' @@ -44,6 +44,14 @@ const getOrCreateUser = async (params: GetOrCreateUserParams): Promise { app.post('/', AdminAddPlatformRequest, async (req, res) => { @@ -17,4 +18,7 @@ const AdminAddPlatformRequest = { schema: { body: AdminAddPlatformRequestBody, }, + config: { + allowedPrincipals: [PrincipalType.SUPER_USER], + }, } diff --git a/packages/backend/src/app/ee/platform/platform.controller.ts b/packages/backend/src/app/ee/platform/platform.controller.ts index 1a51c7594..8fc12a208 100755 --- a/packages/backend/src/app/ee/platform/platform.controller.ts +++ b/packages/backend/src/app/ee/platform/platform.controller.ts @@ -1,13 +1,13 @@ import { FastifyPluginAsyncTypebox, Type } from '@fastify/type-provider-typebox' -import { Platform, UpdatePlatformRequestBody } from '@activepieces/ee-shared' +import { Platform, PlatformWithoutSensitiveData, UpdatePlatformRequestBody } from '@activepieces/ee-shared' import { ApId, Principal, assertEqual } from '@activepieces/shared' import { platformService } from './platform.service' import { platformMustBeOwnedByCurrentUser } from '../authentication/ee-authorization' +import { StatusCodes } from 'http-status-codes' export const platformController: FastifyPluginAsyncTypebox = async (app) => { app.post('/:id', UpdatePlatformRequest, async (req, res) => { await platformMustBeOwnedByCurrentUser.call(app, req, res) - return platformService.update({ id: req.params.id, userId: req.principal.id, @@ -28,7 +28,10 @@ export const platformController: FastifyPluginAsyncTypebox = async (app) => { const buildResponse = ({ platform, principal }: BuildResponseParams): Platform | PlatformBasics => { if (platform.ownerId === principal.id) { - return platform + return { + ...platform, + smtpPassword: undefined, + } } const { id, name, defaultLocale } = platform @@ -48,6 +51,9 @@ const UpdatePlatformRequest = { params: Type.Object({ id: ApId, }), + response: { + [StatusCodes.OK]: PlatformWithoutSensitiveData, + }, }, } diff --git a/packages/backend/src/app/ee/platform/platform.entity.ts b/packages/backend/src/app/ee/platform/platform.entity.ts index 8ca805df5..534506a72 100644 --- a/packages/backend/src/app/ee/platform/platform.entity.ts +++ b/packages/backend/src/app/ee/platform/platform.entity.ts @@ -1,7 +1,7 @@ import { EntitySchema } from 'typeorm' -import { FilteredPieceBehavior, LocalesEnum, Platform } from '@activepieces/ee-shared' -import { ARRAY_COLUMN_TYPE, ApIdSchema, BaseColumnSchemaPart, isPostgres } from '../../database/database-common' -import { User } from '@activepieces/shared' +import { FilteredPieceBehavior, Platform } from '@activepieces/ee-shared' +import { ARRAY_COLUMN_TYPE, ApIdSchema, BaseColumnSchemaPart, JSONB_COLUMN_TYPE, isPostgres } from '../../database/database-common' +import { LocalesEnum, User } from '@activepieces/shared' type PlatformSchema = Platform & { owner: User @@ -91,11 +91,34 @@ export const PlatformEntity = new EntitySchema({ enum: FilteredPieceBehavior, nullable: false, }, + gitSyncEnabled: { + type: Boolean, + nullable: false, + }, defaultLocale: { type: String, enum: LocalesEnum, nullable: true, }, + allowedAuthDomains: { + type: ARRAY_COLUMN_TYPE, + array: isPostgres(), + }, + enforceAllowedAuthDomains: { + type: Boolean, + nullable: false, + }, + ssoEnabled: { + type: Boolean, + nullable: false, + }, + emailAuthEnabled: { + type: Boolean, + nullable: false, + }, + federatedAuthProviders: { + type: JSONB_COLUMN_TYPE, + }, }, indices: [ ], diff --git a/packages/backend/src/app/ee/platform/platform.service.ts b/packages/backend/src/app/ee/platform/platform.service.ts index a81442b0d..964dbe504 100644 --- a/packages/backend/src/app/ee/platform/platform.service.ts +++ b/packages/backend/src/app/ee/platform/platform.service.ts @@ -1,7 +1,7 @@ -import { ActivepiecesError, ErrorCode, ProjectId, UserId, apId, isNil, spreadIfDefined } from '@activepieces/shared' +import { ActivepiecesError, ErrorCode, LocalesEnum, ProjectId, UserId, apId, isNil, spreadIfDefined } from '@activepieces/shared' import { databaseConnection } from '../../database/database-connection' import { PlatformEntity } from './platform.entity' -import { FilteredPieceBehavior, LocalesEnum, Platform, PlatformId, UpdatePlatformRequestBody } from '@activepieces/ee-shared' +import { FilteredPieceBehavior, Platform, PlatformId, UpdatePlatformRequestBody } from '@activepieces/ee-shared' import { defaultTheme } from '../../flags/theme' import { userService } from '../../user/user-service' import { projectService } from '../../project/project-service' @@ -20,12 +20,18 @@ export const platformService = { logoIconUrl: logoIconUrl ?? defaultTheme.logos.logoIconUrl, fullLogoUrl: fullLogoUrl ?? defaultTheme.logos.fullLogoUrl, favIconUrl: favIconUrl ?? defaultTheme.logos.favIconUrl, - embeddingEnabled: true, + embeddingEnabled: false, defaultLocale: LocalesEnum.ENGLISH, + emailAuthEnabled: true, filteredPieceNames: [], + enforceAllowedAuthDomains: false, + allowedAuthDomains: [], filteredPieceBehavior: FilteredPieceBehavior.BLOCKED, showPoweredBy: false, + ssoEnabled: false, + federatedAuthProviders: {}, cloudAuthEnabled: true, + gitSyncEnabled: false, } const savedPlatform = await repo.save(newPlatform) @@ -66,6 +72,7 @@ export const platformService = { ...spreadIfDefined('filteredPieceBehavior', params.filteredPieceBehavior), ...spreadIfDefined('smtpHost', params.smtpHost), ...spreadIfDefined('smtpPort', params.smtpPort), + ...spreadIfDefined('federatedAuthProviders', params.federatedAuthProviders), ...spreadIfDefined('smtpUser', params.smtpUser), ...spreadIfDefined('smtpPassword', params.smtpPassword), ...spreadIfDefined('smtpSenderEmail', params.smtpSenderEmail), @@ -74,6 +81,13 @@ export const platformService = { ...spreadIfDefined('termsOfServiceUrl', params.termsOfServiceUrl), ...spreadIfDefined('cloudAuthEnabled', params.cloudAuthEnabled), ...spreadIfDefined('defaultLocale', params.defaultLocale), + ...spreadIfDefined('showPoweredBy', params.showPoweredBy), + ...spreadIfDefined('gitSyncEnabled', params.gitSyncEnabled), + ...spreadIfDefined('embeddingEnabled', params.embeddingEnabled), + ...spreadIfDefined('ssoEnabled', params.ssoEnabled), + ...spreadIfDefined('emailAuthEnabled', params.emailAuthEnabled), + ...spreadIfDefined('enforceAllowedAuthDomains', params.enforceAllowedAuthDomains), + ...spreadIfDefined('allowedAuthDomains', params.allowedAuthDomains), } return repo.save(updatedPlatform) @@ -155,6 +169,10 @@ type NewPlatform = Omit type UpdateParams = UpdatePlatformRequestBody & { id: PlatformId userId: UserId + showPoweredBy?: boolean + ssoEnabled?: boolean + gitSyncEnabled?: boolean + embeddingEnabled?: boolean } type GetOneByOwnerParams = { diff --git a/packages/backend/src/app/ee/projects/platform-project-controller.ts b/packages/backend/src/app/ee/projects/platform-project-controller.ts index 18be48475..94eeb0a18 100755 --- a/packages/backend/src/app/ee/projects/platform-project-controller.ts +++ b/packages/backend/src/app/ee/projects/platform-project-controller.ts @@ -41,6 +41,7 @@ const platformProjectController: FastifyPluginCallbackTypebox = (fastify, _opts, assertNotNullOrUndefined(platformId, 'platformId') return platformProjectService.getAll({ platformId, + externalId: request.query.externalId, ownerId: undefined, }) }) diff --git a/packages/backend/src/app/ee/user/enterprise-user-controller.ts b/packages/backend/src/app/ee/user/enterprise-user-controller.ts index 385abe36c..1cfd02f57 100644 --- a/packages/backend/src/app/ee/user/enterprise-user-controller.ts +++ b/packages/backend/src/app/ee/user/enterprise-user-controller.ts @@ -2,6 +2,7 @@ import { FastifyPluginAsyncTypebox, Type } from '@fastify/type-provider-typebox' import { ApId, EndpointScope, PrincipalType, SeekPage, UserResponse, assertNotNullOrUndefined } from '@activepieces/shared' import { enterpriseUserService } from './enterprise-user-service' import { StatusCodes } from 'http-status-codes' +import { UpdateUserRequestBody } from '@activepieces/ee-shared' export const enterpriseUserController: FastifyPluginAsyncTypebox = async (app) => { app.get('/', ListUsersRequest, async (req) => { @@ -13,17 +14,18 @@ export const enterpriseUserController: FastifyPluginAsyncTypebox = async (app) = }) }) - app.post('/:id/suspend', SuspendUserRequest, async (req, res) => { + app.post('/:id', UpdateUserRequest, async (req) => { const platformId = req.principal.platform?.id assertNotNullOrUndefined(platformId, 'platformId') - await enterpriseUserService.suspend({ + return enterpriseUserService.update({ id: req.params.id, platformId, + status: req.body.status, }) - return res.status(StatusCodes.NO_CONTENT).send() }) + } const ListUsersRequest = { @@ -38,11 +40,15 @@ const ListUsersRequest = { }, } -const SuspendUserRequest = { +const UpdateUserRequest = { schema: { params: Type.Object({ id: ApId, }), + body: UpdateUserRequestBody, + response: { + [StatusCodes.OK]: UserResponse, + }, }, config: { allowedPrincipals: [PrincipalType.USER, PrincipalType.SERVICE], diff --git a/packages/backend/src/app/ee/user/enterprise-user-service.ts b/packages/backend/src/app/ee/user/enterprise-user-service.ts index a412ba0a5..fb7695c17 100644 --- a/packages/backend/src/app/ee/user/enterprise-user-service.ts +++ b/packages/backend/src/app/ee/user/enterprise-user-service.ts @@ -2,7 +2,6 @@ import { ActivepiecesError, ErrorCode, SeekPage, User, UserId, UserStatus } from import { PlatformId } from '@activepieces/ee-shared' import { databaseConnection } from '../../database/database-connection' import { UserEntity } from '../../user/user-entity' -import { FindOptionsWhere } from 'typeorm' const repo = databaseConnection.getRepository(UserEntity) @@ -19,17 +18,13 @@ export const enterpriseUserService = { } }, - async suspend({ id, platformId }: DeleteParams): Promise { - const updateCriteria: FindOptionsWhere = { + async update({ id, status, platformId }: UpdateParams): Promise { + const updateResult = await repo.update({ id, platformId, - status: UserStatus.VERIFIED, - } - - const updateResult = await repo.update(updateCriteria, { - status: UserStatus.SUSPENDED, + }, { + status, }) - if (updateResult.affected !== 1) { throw new ActivepiecesError({ code: ErrorCode.ENTITY_NOT_FOUND, @@ -39,6 +34,10 @@ export const enterpriseUserService = { }, }) } + return repo.findOneByOrFail({ + id, + platformId, + }) }, } @@ -46,7 +45,8 @@ type ListParams = { platformId: PlatformId } -type DeleteParams = { +type UpdateParams = { id: UserId + status: UserStatus platformId: PlatformId } diff --git a/packages/backend/src/app/flags/flag.service.ts b/packages/backend/src/app/flags/flag.service.ts index 55c4ebcfc..ced601166 100755 --- a/packages/backend/src/app/flags/flag.service.ts +++ b/packages/backend/src/app/flags/flag.service.ts @@ -7,7 +7,6 @@ import axios from 'axios' import { webhookService } from '../webhooks/webhook-service' import { getEdition } from '../helper/secret-helper' import { defaultTheme } from './theme' -import { showThirdPartyProvidersMap } from '../ee/authentication/federated-authn/authn-provider/authn-provider' const flagRepo = databaseConnection.getRepository(FlagEntity) @@ -43,6 +42,12 @@ export const flagService = { created, updated, }, + { + id: ApFlagId.SHOW_GIT_SYNC, + value: true, + created, + updated, + }, { id: ApFlagId.CLOUD_AUTH_ENABLED, value: system.getBoolean(SystemProp.CLOUD_AUTH_ENABLED) ?? true, @@ -75,8 +80,7 @@ export const flagService = { }, { id: ApFlagId.THIRD_PARTY_AUTH_PROVIDERS_TO_SHOW_MAP, - //show only for cloud and hide it for platform users in flags hook - value: getEdition() === ApEdition.CLOUD ? showThirdPartyProvidersMap : {}, + value: {}, created, updated, }, @@ -92,6 +96,12 @@ export const flagService = { created, updated, }, + { + id: ApFlagId.EMAIL_AUTH_ENABLED, + value: true, + created, + updated, + }, { id: ApFlagId.THEME, value: defaultTheme, @@ -187,7 +197,9 @@ export const flagService = { return flags }, getThirdPartyRedirectUrl(): string { - return `${system.get(SystemProp.FRONTEND_URL)}/redirect` + const frontendUrl = system.get(SystemProp.FRONTEND_URL) + const trimmedFrontendUrl = frontendUrl?.endsWith('/') ? frontendUrl.slice(0, -1) : frontendUrl + return `${trimmedFrontendUrl}/redirect` }, async getCurrentRelease(): Promise { const packageJson = await import('package.json') diff --git a/packages/backend/src/app/flows/flow-run/flow-run-controller.ts b/packages/backend/src/app/flows/flow-run/flow-run-controller.ts index 1201e864c..3098a15e5 100755 --- a/packages/backend/src/app/flows/flow-run/flow-run-controller.ts +++ b/packages/backend/src/app/flows/flow-run/flow-run-controller.ts @@ -1,7 +1,7 @@ import { FastifyReply, FastifyRequest } from 'fastify' import { FastifyPluginCallbackTypebox, Type } from '@fastify/type-provider-typebox' -import { TestFlowRunRequestBody, FlowRunId, ListFlowRunsRequestQuery, ApId, ALL_PRINICPAL_TYPES } from '@activepieces/shared' -import { ActivepiecesError, ErrorCode } from '@activepieces/shared' +import { TestFlowRunRequestBody, FlowRunId, ListFlowRunsRequestQuery, ApId, ALL_PRINICPAL_TYPES, ExecutionType } from '@activepieces/shared' +import { ActivepiecesError, ErrorCode, RetryFlowRequestBody } from '@activepieces/shared' import { flowRunService } from './flow-run-service' const DEFAULT_PAGING_LIMIT = 10 @@ -30,6 +30,15 @@ const ResumeFlowRunRequest = { }, } +const RetryFlowRequest = { + schema: { + params: Type.Object({ + id: ApId, + }), + querystring: RetryFlowRequestBody, + }, +} + export const flowRunController: FastifyPluginCallbackTypebox = (app, _options, done): void => { app.post('/test', TestFlowRunRequest, async (req) => { const { projectId } = req.principal @@ -77,9 +86,20 @@ export const flowRunController: FastifyPluginCallbackTypebox = (app, _options, d app.all('/:id/resume', ResumeFlowRunRequest, async (req) => { - await flowRunService.resume({ + await flowRunService.addToQueue({ + flowRunId: req.params.id, + payload: { + action: req.query.action, + }, + executionType: ExecutionType.RESUME, + }) + }) + + + app.post('/:id/retry', RetryFlowRequest, async (req) => { + await flowRunService.retry({ flowRunId: req.params.id, - action: req.query.action, + strategy: req.query.strategy, }) }) diff --git a/packages/backend/src/app/flows/flow-run/flow-run-service.ts b/packages/backend/src/app/flows/flow-run/flow-run-service.ts index 7da018dcb..30904ba6b 100644 --- a/packages/backend/src/app/flows/flow-run/flow-run-service.ts +++ b/packages/backend/src/app/flows/flow-run/flow-run-service.ts @@ -18,6 +18,7 @@ import { ExecutionType, isNil, RunTerminationReason, + FlowRetryStrategy, } from '@activepieces/shared' import { APArrayContains, databaseConnection } from '../../database/database-connection' import { flowVersionService } from '../../flows/flow-version/flow-version.service' @@ -55,6 +56,14 @@ const getFlowRunOrCreate = async (params: GetOrCreateParams): Promise { + const flowRun = await flowRunRepo.findOneByOrFail({ id: flowRunId }) + const flowVersion = await flowVersionService.getLatestLockedVersionOrThrow(flowRun.flowId) + await flowRunRepo.update(flowRunId, { + flowVersionId: flowVersion.id, + }) +} + export const flowRunService = { async list({ projectId, flowId, status, cursor, limit, tags }: ListParams): Promise> { const decodedCursor = paginationHelper.decodeCursor(cursor) @@ -80,9 +89,31 @@ export const flowRunService = { const { data, cursor: newCursor } = await paginator.paginate(query) return paginationHelper.createPage(data, newCursor) }, - async resume({ flowRunId, action }: { + async retry({ flowRunId, strategy }: RetryParams): Promise { + switch (strategy) { + case FlowRetryStrategy.FROM_FAILED_STEP: + await flowRunService.addToQueue({ + flowRunId, + payload: {}, + executionType: ExecutionType.RESUME, + }) + break + case FlowRetryStrategy.ON_LATEST_VERSION: { + await updateFlowRunToLatestFlowVersionId(flowRunId) + await flowRunService.addToQueue({ + flowRunId, + payload: {}, + executionType: ExecutionType.BEGIN, + + }) + break + } + } + }, + async addToQueue({ flowRunId, payload, executionType }: { flowRunId: FlowRunId - action: string + payload: Record + executionType: ExecutionType }): Promise { logger.info(`[FlowRunService#resume] flowRunId=${flowRunId}`) @@ -100,13 +131,11 @@ export const flowRunService = { } await flowRunService.start({ - payload: { - action, - }, + payload, flowRunId: flowRunToResume.id, projectId: flowRunToResume.projectId, flowVersionId: flowRunToResume.flowVersionId, - executionType: ExecutionType.RESUME, + executionType, environment: RunEnvironment.PRODUCTION, }) }, @@ -231,7 +260,7 @@ export const flowRunService = { async getAllProdRuns(params: GetAllProdRuns): Promise { const { projectId, created } = params - + const sumOfTasks = await flowRunRepo.createQueryBuilder('flow_run') .select('COALESCE(SUM(flow_run.tasks), 0)', 'tasks') .where({ @@ -240,10 +269,9 @@ export const flowRunService = { created: MoreThanOrEqual(created), }) .getRawOne() - + return Number(sumOfTasks.tasks) }, - } type GetOrCreateParams = { @@ -290,6 +318,11 @@ type PauseParams = { pauseMetadata: PauseMetadata } +type RetryParams = { + flowRunId: FlowRunId + strategy: FlowRetryStrategy +} + type GetAllProdRuns = { projectId: ProjectId created: string diff --git a/packages/backend/src/app/flows/flow-version/flow-version.service.ts b/packages/backend/src/app/flows/flow-version/flow-version.service.ts index 68da7e54e..feba8fca5 100755 --- a/packages/backend/src/app/flows/flow-version/flow-version.service.ts +++ b/packages/backend/src/app/flows/flow-version/flow-version.service.ts @@ -5,6 +5,7 @@ import { ActionType, apId, BranchActionSettingsWithValidation, + Cursor, flowHelper, FlowId, FlowOperationRequest, @@ -18,6 +19,7 @@ import { PieceTriggerSettings, ProjectId, TriggerType, + SeekPage, UserId, } from '@activepieces/shared' import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity' @@ -31,10 +33,12 @@ import { pieceMetadataService } from '../../pieces/piece-metadata-service' import dayjs from 'dayjs' import { logger } from '../../helper/logger' import { stepFileService } from '../step-file/step-file.service' +import { buildPaginator } from '../../helper/pagination/build-paginator' +import { paginationHelper } from '../../helper/pagination/pagination-utils' const branchSettingsValidator = TypeCompiler.Compile(BranchActionSettingsWithValidation) const loopSettingsValidator = TypeCompiler.Compile(LoopOnItemsActionSettingsWithValidation) -const flowVersionRepo = databaseConnection.getRepository(FlowVersionEntity) +const flowVersionRepo = databaseConnection.getRepository(FlowVersionEntity) export const flowVersionService = { async lockPieceVersions(projectId: ProjectId, mutatedFlowVersion: FlowVersion): Promise { @@ -62,8 +66,17 @@ export const flowVersionService = { }, async applyOperation(userId: UserId, projectId: ProjectId, flowVersion: FlowVersion, userOperation: FlowOperationRequest): Promise { let operations: FlowOperationRequest[] = [] - let mutatedFlowVersion = flowVersion + let mutatedFlowVersion: FlowVersion = flowVersion switch (userOperation.type) { + case FlowOperationType.USE_AS_DRAFT: { + const previousVersion = await flowVersionService.getFlowVersionOrThrow({ + flowId: flowVersion.flowId, + versionId: userOperation.request.versionId, + removeSecrets: false, + }) + operations = handleImportFlowOperation(flowVersion, previousVersion) + break + } case FlowOperationType.IMPORT_FLOW: operations = handleImportFlowOperation(flowVersion, userOperation.request) break @@ -75,7 +88,7 @@ export const flowVersionService = { operations = [userOperation] break case FlowOperationType.DUPLICATE_ACTION: - mutatedFlowVersion = await this.getFlowVersion({ + mutatedFlowVersion = await this.getFlowVersionOrThrow({ flowId: flowVersion.flowId, versionId: flowVersion.id, }) @@ -100,21 +113,50 @@ export const flowVersionService = { id, }) }, + async getLatestLockedVersionOrThrow(flowId: FlowId): Promise { + return flowVersionRepo.findOneOrFail({ + where: { + flowId, + state: FlowVersionState.LOCKED, + }, + order: { + created: 'DESC', + }, + }) + }, async getOneOrThrow(id: FlowVersionId): Promise { const flowVersion = await flowVersionService.getOne(id) + if (isNil(flowVersion)) { throw new ActivepiecesError({ - code: ErrorCode.FLOW_VERSION_NOT_FOUND, + code: ErrorCode.ENTITY_NOT_FOUND, params: { - id, + entityId: id, + entityType: 'FlowVersion', }, }) } return flowVersion }, - async getFlowVersion({ flowId, versionId, removeSecrets = false }: { flowId: FlowId, versionId: FlowVersionId | undefined, removeSecrets?: boolean }): Promise { - let flowVersion = await flowVersionRepo.findOneOrFail({ + async list({ cursorRequest, limit, flowId }: { cursorRequest: Cursor | null, limit: number, flowId: string }): Promise> { + const decodedCursor = paginationHelper.decodeCursor(cursorRequest) + const paginator = buildPaginator({ + entity: FlowVersionEntity, + query: { + limit, + order: 'DESC', + afterCursor: decodedCursor.nextCursor, + beforeCursor: decodedCursor.previousCursor, + }, + }) + const paginationResult = await paginator.paginate(flowVersionRepo.createQueryBuilder('flow_version').where({ + flowId, + })) + return paginationHelper.createPage(paginationResult.data, paginationResult.cursor) + }, + async getFlowVersionOrThrow({ flowId, versionId, removeSecrets = false }: GetFlowVersionOrThrowParams): Promise { + let flowVersion: FlowVersion | null = await flowVersionRepo.findOne({ where: { flowId, id: versionId, @@ -124,15 +166,28 @@ export const flowVersionService = { created: 'DESC', }, }) + + if (isNil(flowVersion)) { + throw new ActivepiecesError({ + code: ErrorCode.ENTITY_NOT_FOUND, + params: { + entityId: versionId, + entityType: 'FlowVersion', + message: `flowId=${flowId}`, + }, + }) + } + if (removeSecrets) { flowVersion = await removeSecretsFromFlow(flowVersion) } + return flowVersion }, async createEmptyVersion(flowId: FlowId, request: { displayName: string }): Promise { - const flowVersion: Partial = { + const flowVersion: NewFlowVersion = { id: apId(), displayName: request.displayName, flowId, @@ -322,7 +377,11 @@ async function validateAction({ projectId, settings }: { projectId: ProjectId, s if (isNil(action)) { return false } - return validateProps(action.props, settings.input) + const props = action.props + if (!isNil(piece.auth) && action.requireAuth) { + props.auth = piece.auth + } + return validateProps(props, settings.input) } async function validateTrigger({ settings, projectId }: { settings: PieceTriggerSettings, projectId: ProjectId }): Promise { @@ -348,7 +407,11 @@ async function validateTrigger({ settings, projectId }: { settings: PieceTrigger if (isNil(trigger)) { return false } - return validateProps(trigger.props, settings.input) + const props = trigger.props + if (!isNil(piece.auth)) { + props.auth = piece.auth + } + return validateProps(props, settings.input) } function validateProps(props: PiecePropertyMap, input: Record): boolean { @@ -423,3 +486,10 @@ function buildSchema(props: PiecePropertyMap): TSchema { return Type.Object(propsSchema) } +type GetFlowVersionOrThrowParams = { + flowId: FlowId + versionId: FlowVersionId | undefined + removeSecrets?: boolean +} + +type NewFlowVersion = Omit diff --git a/packages/backend/src/app/flows/flow.module.ts b/packages/backend/src/app/flows/flow.module.ts index bf4524496..25581e668 100755 --- a/packages/backend/src/app/flows/flow.module.ts +++ b/packages/backend/src/app/flows/flow.module.ts @@ -2,11 +2,11 @@ import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' import { flowController } from './flow/flow.controller' import { stepRunController } from './step-run/step-run-controller' import { folderController } from './folder/folder.controller' -import { entitiesMustBeOwnedByCurrentProject } from '../authentication/authorization' +import { flowVersionController } from './flow/flow-version.controller' export const flowModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(stepRunController, { prefix: '/v1/step-run' }) - app.addHook('preSerialization', entitiesMustBeOwnedByCurrentProject) + await app.register(flowVersionController, { prefix: '/v1/flows' }) await app.register(flowController, { prefix: '/v1/flows' }) await app.register(folderController, { prefix: '/v1/folders' }) } diff --git a/packages/backend/src/app/flows/flow/flow-service-hooks.ts b/packages/backend/src/app/flows/flow/flow-service-hooks.ts index de75ab232..aaf898b5f 100644 --- a/packages/backend/src/app/flows/flow/flow-service-hooks.ts +++ b/packages/backend/src/app/flows/flow/flow-service-hooks.ts @@ -1,4 +1,4 @@ -import { Flow, FlowScheduleOptions, FlowStatus, ScheduleOptions, ScheduleType, assertNotNullOrUndefined, isNil } from '@activepieces/shared' +import { Flow, FlowScheduleOptions, FlowStatus, FlowVersion, ScheduleOptions, ScheduleType, assertNotNullOrUndefined, isNil } from '@activepieces/shared' import { flowVersionService } from '../flow-version/flow-version.service' import { triggerUtils } from '../../helper/trigger-utils' @@ -6,7 +6,7 @@ export const flowServiceHooks = { async preUpdateStatus({ flowToUpdate, newStatus }: PreUpdateStatusParams): Promise { assertNotNullOrUndefined(flowToUpdate.publishedVersionId, 'publishedVersionId') - const publishedFlowVersion = await flowVersionService.getFlowVersion({ + const publishedFlowVersion = await flowVersionService.getFlowVersionOrThrow({ flowId: flowToUpdate.id, versionId: flowToUpdate.publishedVersionId, }) @@ -47,7 +47,7 @@ export const flowServiceHooks = { } }, - async preUpdatePublishedVersionId({ flowToUpdate, newPublishedVersionId }: PreUpdatePublishedVersionIdParams): Promise { + async preUpdatePublishedVersionId({ flowToUpdate, flowVersionToPublish }: PreUpdatePublishedVersionIdParams): Promise { if (flowToUpdate.status === FlowStatus.ENABLED && flowToUpdate.publishedVersionId) { await triggerUtils.disable({ flowVersion: await flowVersionService.getOneOrThrow(flowToUpdate.publishedVersionId), @@ -56,11 +56,6 @@ export const flowServiceHooks = { }) } - const flowVersionToPublish = await flowVersionService.getFlowVersion({ - flowId: flowToUpdate.id, - versionId: newPublishedVersionId, - }) - const enableResult = await triggerUtils.enable({ flowVersion: flowVersionToPublish, projectId: flowToUpdate.projectId, @@ -88,7 +83,7 @@ export const flowServiceHooks = { return } - const publishedFlowVersion = await flowVersionService.getFlowVersion({ + const publishedFlowVersion = await flowVersionService.getFlowVersionOrThrow({ flowId: flowToDelete.id, versionId: flowToDelete.publishedVersionId, }) @@ -110,7 +105,7 @@ type PreUpdateStatusParams = PreUpdateParams & { } type PreUpdatePublishedVersionIdParams = PreUpdateParams & { - newPublishedVersionId: string + flowVersionToPublish: FlowVersion } type PreUpdateReturn = { diff --git a/packages/backend/src/app/flows/flow/flow-version.controller.ts b/packages/backend/src/app/flows/flow/flow-version.controller.ts new file mode 100755 index 000000000..0f6917567 --- /dev/null +++ b/packages/backend/src/app/flows/flow/flow-version.controller.ts @@ -0,0 +1,36 @@ +import { + ListFlowVersionRequest, + SeekPage, +} from '@activepieces/shared' +import { StatusCodes } from 'http-status-codes' +import { flowService } from './flow.service' +import { FastifyPluginAsyncTypebox, Type } from '@fastify/type-provider-typebox' +import { flowVersionService } from '../flow-version/flow-version.service' +import { FlowVersionMetadata } from '@activepieces/shared' + +const DEFUALT_PAGE_SIZE = 10 + +export const flowVersionController: FastifyPluginAsyncTypebox = async (fastify) => { + + fastify.get('/:flowId/versions', { + schema: { + params: Type.Object({ + flowId: Type.String(), + }), + querystring: ListFlowVersionRequest, + response: { + [StatusCodes.OK]: SeekPage(FlowVersionMetadata), + }, + }, + + }, async (request) => { + const flow = await flowService.getOneOrThrow({ id: request.params.flowId, projectId: request.principal.projectId }) + return flowVersionService.list({ + flowId: flow.id, + limit: request.query.limit ?? DEFUALT_PAGE_SIZE, + cursorRequest: request.query.cursor ?? null, + }) + }) + + +} diff --git a/packages/backend/src/app/flows/flow/flow.controller.ts b/packages/backend/src/app/flows/flow/flow.controller.ts index 724ce404b..5d3e56479 100755 --- a/packages/backend/src/app/flows/flow/flow.controller.ts +++ b/packages/backend/src/app/flows/flow/flow.controller.ts @@ -6,6 +6,8 @@ import { GetFlowQueryParamsRequest, ListFlowsRequest, PopulatedFlow, + PrincipalType, + SeekPage, UpdateFlowStatusRequest, } from '@activepieces/shared' import { StatusCodes } from 'http-status-codes' @@ -76,6 +78,7 @@ export const flowController: FastifyPluginAsyncTypebox = async (app) => { folderId: request.query.folderId, cursorRequest: request.query.cursor ?? null, limit: request.query.limit ?? DEFAULT_PAGE_SIZE, + status: request.query.status, }) }) @@ -113,8 +116,16 @@ export const flowController: FastifyPluginAsyncTypebox = async (app) => { } const CreateFlowRequestOptions = { + config: { + allowedPrincipals: [PrincipalType.USER, PrincipalType.SERVICE], + }, schema: { + tags: ['flows'], + description: 'Create a flow', body: CreateFlowRequest, + response: { + [StatusCodes.CREATED]: PopulatedFlow, + }, }, } @@ -145,9 +156,16 @@ const UpdateFlowPublishedVersionIdRequestOptions = { } const ListFlowsRequestOptions = { - description: 'List flows', + config: { + allowedPrincipals: [PrincipalType.USER, PrincipalType.SERVICE], + }, schema: { + tags: ['flows'], + description: 'List flows', querystring: ListFlowsRequest, + response: { + [StatusCodes.OK]: SeekPage(PopulatedFlow), + }, }, } @@ -169,8 +187,12 @@ const GetFlowTemplateRequestOptions = { } const GetFlowRequestOptions = { - description: 'Get a flow by id', + config: { + allowedPrincipals: [PrincipalType.USER, PrincipalType.SERVICE], + }, schema: { + tags: ['flows'], + description: 'Get a flow by id', params: Type.Object({ id: ApId, }), @@ -182,7 +204,11 @@ const GetFlowRequestOptions = { } const DeleteFlowRequestOptions = { + config: { + allowedPrincipals: [PrincipalType.USER, PrincipalType.SERVICE], + }, schema: { + tags: ['flows'], description: 'Delete a flow', params: Type.Object({ id: ApId, diff --git a/packages/backend/src/app/flows/flow/flow.service.ts b/packages/backend/src/app/flows/flow/flow.service.ts index 23c9b0bdf..060ea44ff 100755 --- a/packages/backend/src/app/flows/flow/flow.service.ts +++ b/packages/backend/src/app/flows/flow/flow.service.ts @@ -10,7 +10,6 @@ import { FlowOperationRequest, FlowOperationType, FlowTemplateWithoutProjectInformation, - FlowVersion, FlowVersionId, FlowVersionState, ProjectId, @@ -18,6 +17,7 @@ import { TelemetryEventName, UserId, PopulatedFlow, + FlowVersion, } from '@activepieces/shared' import { flowVersionService } from '../flow-version/flow-version.service' import { paginationHelper } from '../../helper/pagination/pagination-utils' @@ -65,8 +65,9 @@ export const flowService = { } }, - async list({ projectId, cursorRequest, limit, folderId }: ListParams): Promise> { + async list({ projectId, cursorRequest, limit, folderId, status }: ListParams): Promise> { const decodedCursor = paginationHelper.decodeCursor(cursorRequest) + const paginator = buildPaginator({ entity: FlowEntity, query: { @@ -76,24 +77,33 @@ export const flowService = { beforeCursor: decodedCursor.previousCursor, }, }) + const queryWhere: Record = { projectId } + if (folderId !== undefined) { queryWhere.folderId = (folderId === 'NULL' ? IsNull() : folderId) } + if (status !== undefined) { + queryWhere.status = status + } + const paginationResult = await paginator.paginate(flowRepo.createQueryBuilder('flow').where(queryWhere)) - const flowVersionsPromises: Promise[] = [] - paginationResult.data.forEach((flow) => { - flowVersionsPromises.push(flowVersionService.getFlowVersion({ + + const populatedFlowPromises = paginationResult.data.map(async (flow) => { + const version = await flowVersionService.getFlowVersionOrThrow({ flowId: flow.id, versionId: undefined, - })) + }) + + return { + ...flow, + version, + } }) - const versions: (FlowVersion | null)[] = await Promise.all(flowVersionsPromises) - const populatedFlows: PopulatedFlow[] = paginationResult.data.map((flow, idx) => ({ - ...flow, - version: versions[idx]!, - })) + + const populatedFlows = await Promise.all(populatedFlowPromises) + return paginationHelper.createPage(populatedFlows, paginationResult.cursor) }, @@ -120,7 +130,7 @@ export const flowService = { return null } - const flowVersion = await flowVersionService.getFlowVersion({ + const flowVersion = await flowVersionService.getFlowVersionOrThrow({ flowId: id, versionId, removeSecrets, @@ -151,13 +161,13 @@ export const flowService = { }) } else { - let lastVersion = await flowVersionService.getFlowVersion({ + let lastVersion = await flowVersionService.getFlowVersionOrThrow({ flowId: id, versionId: undefined, }) if (lastVersion.state === FlowVersionState.LOCKED) { - const lastVersionWithArtifacts = await flowVersionService.getFlowVersion({ + const lastVersionWithArtifacts = await flowVersionService.getFlowVersionOrThrow({ flowId: id, versionId: undefined, }) @@ -226,25 +236,23 @@ export const flowService = { try { const flowToUpdate = await this.getOneOrThrow({ id, projectId }) - const lockedFlow = await this.update({ - id, - userId, - projectId, - operation: { - type: FlowOperationType.LOCK_FLOW, - request: { - flowId: id, - }, - }, - lock: false, + const flowVersionToPublish = await flowVersionService.getFlowVersionOrThrow({ + flowId: id, + versionId: undefined, }) const { scheduleOptions } = await hooks.preUpdatePublishedVersionId({ flowToUpdate, - newPublishedVersionId: lockedFlow.version.id, + flowVersionToPublish, + }) + + const lockedFlowVersion = await lockFlowVersionIfNotLocked({ + flowVersion: flowVersionToPublish, + userId, + projectId, }) - flowToUpdate.publishedVersionId = lockedFlow.version.id + flowToUpdate.publishedVersionId = lockedFlowVersion.id flowToUpdate.status = FlowStatus.ENABLED flowToUpdate.schedule = scheduleOptions @@ -252,7 +260,7 @@ export const flowService = { return { ...updatedFlow, - version: lockedFlow.version, + version: lockedFlowVersion, } } finally { @@ -321,6 +329,19 @@ export const flowService = { }, } +const lockFlowVersionIfNotLocked = async ({ flowVersion, userId, projectId }: LockFlowVersionIfNotLockedParams): Promise => { + if (flowVersion.state === FlowVersionState.LOCKED) { + return flowVersion + } + + return flowVersionService.applyOperation(userId, projectId, flowVersion, { + type: FlowOperationType.LOCK_FLOW, + request: { + flowId: flowVersion.flowId, + }, + }) +} + const assertFlowIsNotNull: (flow: T | null) => asserts flow is T = (flow: T | null) => { if (isNil(flow)) { throw new ActivepiecesError({ @@ -340,6 +361,7 @@ type ListParams = { cursorRequest: Cursor | null limit: number folderId: string | undefined + status: FlowStatus | undefined } type GetOneParams = { @@ -389,3 +411,9 @@ type DeleteParams = { } type NewFlow = Omit + +type LockFlowVersionIfNotLockedParams = { + flowVersion: FlowVersion + userId: UserId + projectId: ProjectId +} diff --git a/packages/backend/src/app/flows/folder/folder.controller.ts b/packages/backend/src/app/flows/folder/folder.controller.ts index 9e1be82a4..9a4d36989 100644 --- a/packages/backend/src/app/flows/folder/folder.controller.ts +++ b/packages/backend/src/app/flows/folder/folder.controller.ts @@ -4,6 +4,7 @@ import { FastifyRequest } from 'fastify' import { flowFolderService as folderService } from './folder.service' import { StatusCodes } from 'http-status-codes' import { Static, Type } from '@sinclair/typebox' +import { entitiesMustBeOwnedByCurrentProject } from '../../authentication/authorization' const DEFUALT_PAGE_SIZE = 10 @@ -15,7 +16,7 @@ const FolderIdParam = Type.Object({ type FolderIdParam = Static export const folderController: FastifyPluginAsyncTypebox = async (fastify) => { - + fastify.addHook('preSerialization', entitiesMustBeOwnedByCurrentProject) fastify.post( '/', { diff --git a/packages/backend/src/app/helper/engine-helper.ts b/packages/backend/src/app/helper/engine-helper.ts index cb9663bc8..7f525d269 100644 --- a/packages/backend/src/app/helper/engine-helper.ts +++ b/packages/backend/src/app/helper/engine-helper.ts @@ -21,12 +21,13 @@ import { EngineTestOperation, BeginExecuteFlowOperation, ResumeExecuteFlowOperation, - ExcuteStepOperation, + ExecuteStepOperation, flowHelper, Action, assertNotNullOrUndefined, ActionType, FlowVersion, + ExecuteFlowOperation, } from '@activepieces/shared' import { Sandbox } from '../workers/sandbox' import { accessTokenManager } from '../authentication/lib/access-token-manager' @@ -162,7 +163,7 @@ export const engineHelper = { sandboxId: sandbox.boxId, }, '[EngineHelper#executeFlow]') - const input = { + const input: ExecuteFlowOperation = { ...operation, workerToken: await generateWorkerToken({ projectId: operation.projectId }), serverUrl: await getServerUrl(), @@ -284,7 +285,7 @@ export const engineHelper = { ) }, - async executeAction(operation: Omit): Promise> { + async executeAction(operation: Omit): Promise> { logger.debug({ flowVersionId: operation.flowVersion.id, stepName: operation.stepName, @@ -292,8 +293,8 @@ export const engineHelper = { const lockedFlowVersion = await lockPieceAction(operation) const step = flowHelper.getStep(lockedFlowVersion, operation.stepName) as Action | undefined assertNotNullOrUndefined(step, 'Step not found') - const sandbox = await getSandboxForAction(operation.projectId, step) - const input: ExcuteStepOperation = { + const sandbox = await getSandboxForAction(operation.projectId, operation.flowVersion.flowId, step) + const input: ExecuteStepOperation = { flowVersion: lockedFlowVersion, stepName: operation.stepName, projectId: operation.projectId, @@ -373,7 +374,7 @@ async function lockPieceAction({ projectId, flowVersion, stepName }: { projectId }) } -async function getSandboxForAction(projectId: string, action: Action): Promise { +async function getSandboxForAction(projectId: string, flowId: string, action: Action): Promise { switch (action.type) { case ActionType.PIECE:{ const { packageType, pieceType, pieceName, pieceVersion } = action.settings @@ -397,6 +398,8 @@ async function getSandboxForAction(projectId: string, action: Action): Promise { - const { payload, flowVersion, projectId, simulate } = params - if (simulate) return null + const { payload, flowVersion, projectId } = params const flowTrigger = flowVersion.trigger if (flowTrigger.type === TriggerType.PIECE) { const pieceTrigger = await getPieceTrigger({ @@ -64,8 +63,7 @@ export const triggerUtils = { if (isNil(handshakeConfig)) { return null } - const strategy = - handshakeConfig.strategy ?? WebhookHandshakeStrategy.NONE + const strategy = handshakeConfig.strategy ?? WebhookHandshakeStrategy.NONE switch (strategy) { case WebhookHandshakeStrategy.HEADER_PRESENT: { if ( diff --git a/packages/backend/src/app/project/project-module.ts b/packages/backend/src/app/project/project-module.ts index 79a7df130..e7305587e 100755 --- a/packages/backend/src/app/project/project-module.ts +++ b/packages/backend/src/app/project/project-module.ts @@ -1,8 +1,8 @@ import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' import { projectController } from './project-controller' +import { projectWorkerController } from './project-worker-controller' export const projectModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(projectController, { prefix: '/v1/users/projects' }) + await app.register(projectWorkerController, { prefix: '/v1/worker/project' }) } - - diff --git a/packages/backend/src/app/project/project-worker-controller.ts b/packages/backend/src/app/project/project-worker-controller.ts new file mode 100644 index 000000000..465d1ca20 --- /dev/null +++ b/packages/backend/src/app/project/project-worker-controller.ts @@ -0,0 +1,16 @@ +import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' +import { PrincipalType } from '@activepieces/shared' +import { projectService } from './project-service' + +export const projectWorkerController: FastifyPluginAsyncTypebox = async (app) => { + app.get('/', GetWorkerProjectRequest, async (req) => { + const projectId = req.principal.projectId + return projectService.getOneOrThrow(projectId) + }) +} + +const GetWorkerProjectRequest = { + config: { + allowedPrincipals: [PrincipalType.WORKER], + }, +} diff --git a/packages/backend/src/app/user/user-entity.ts b/packages/backend/src/app/user/user-entity.ts index b0b0ac9b4..700dd7d00 100755 --- a/packages/backend/src/app/user/user-entity.ts +++ b/packages/backend/src/app/user/user-entity.ts @@ -22,6 +22,9 @@ export const UserEntity = new EntitySchema({ password: { type: String, }, + verified: { + type: Boolean, + }, status: { type: String, }, diff --git a/packages/backend/src/app/user/user-service.ts b/packages/backend/src/app/user/user-service.ts index b9fa339fa..f343ad595 100755 --- a/packages/backend/src/app/user/user-service.ts +++ b/packages/backend/src/app/user/user-service.ts @@ -1,4 +1,4 @@ -import { apId, ExternalUserRequest, SignUpRequest, User, UserId, UserMeta, UserStatus, isNil } from '@activepieces/shared' +import { apId, SignUpRequest, User, UserId, UserMeta, UserStatus, isNil, ActivepiecesError, ErrorCode } from '@activepieces/shared' import { passwordHasher } from '../authentication/lib/password-hasher' import { databaseConnection } from '../database/database-connection' import { UserEntity } from './user-entity' @@ -31,6 +31,7 @@ export const userService = { const user: NewUser = { id: apId(), ...params, + status: UserStatus.ACTIVE, password: hashedPassword, } @@ -39,10 +40,17 @@ export const userService = { async verify({ id }: IdParams): Promise { const user = await userRepo.findOneByOrFail({ id }) - + if (user.verified) { + throw new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'User is already verified', + }, + }) + } return userRepo.save({ ...user, - status: UserStatus.VERIFIED, + verified: true, }) }, @@ -105,7 +113,7 @@ export const userService = { } type CreateParams = SignUpRequest & { - status: UserStatus + verified: boolean platformId: PlatformId | null externalId?: string } diff --git a/packages/backend/src/app/webhooks/webhook-controller.ts b/packages/backend/src/app/webhooks/webhook-controller.ts index 11b76f278..b9b532f9a 100644 --- a/packages/backend/src/app/webhooks/webhook-controller.ts +++ b/packages/backend/src/app/webhooks/webhook-controller.ts @@ -26,7 +26,7 @@ export const webhookController: FastifyPluginAsyncTypebox = async (app) => { async (request: FastifyRequest<{ Params: WebhookUrlParams }>, reply) => { const flow = await getFlowOrThrow(request.params.flowId) const payload = await convertRequest(request) - const isHandshake = await handshakeHandler(flow, payload, reply) + const isHandshake = await handshakeHandler(flow, payload, false, reply) if (isHandshake) { return } @@ -62,7 +62,7 @@ export const webhookController: FastifyPluginAsyncTypebox = async (app) => { async (request: FastifyRequest<{ Params: WebhookUrlParams }>, reply) => { const flow = await getFlowOrThrow(request.params.flowId) const payload = await convertRequest(request) - const isHandshake = await handshakeHandler(flow, payload, reply) + const isHandshake = await handshakeHandler(flow, payload, false, reply) if (isHandshake) { return } @@ -85,7 +85,7 @@ export const webhookController: FastifyPluginAsyncTypebox = async (app) => { async (request: FastifyRequest<{ Querystring: WebhookUrlParams }>, reply) => { const flow = await getFlowOrThrow(request.query.flowId) const payload = await convertRequest(request) - const isHandshake = await handshakeHandler(flow, payload, reply) + const isHandshake = await handshakeHandler(flow, payload, false, reply) if (isHandshake) { return } @@ -108,6 +108,11 @@ export const webhookController: FastifyPluginAsyncTypebox = async (app) => { async (request: FastifyRequest<{ Params: WebhookUrlParams }>, reply) => { logger.debug(`[WebhookController#simulate] flowId=${request.params.flowId}`) const flow = await getFlowOrThrow(request.params.flowId) + const payload = await convertRequest(request) + const isHandshake = await handshakeHandler(flow, payload, true, reply) + if (isHandshake) { + return + } await webhookService.simulationCallback({ flow, payload: { @@ -150,12 +155,13 @@ const convertBody = async (request: FastifyRequest): Promise => { } -async function handshakeHandler(flow: Flow, payload: EventPayload, reply: FastifyReply): Promise { +async function handshakeHandler(flow: Flow, payload: EventPayload, simulate: boolean, reply: FastifyReply): Promise { const handshakeResponse = await webhookService.handshake({ flow, payload, + simulate, }) - if (handshakeResponse !== null) { + if (!isNil(handshakeResponse)) { reply = reply.status(handshakeResponse.status) if (handshakeResponse.headers !== undefined) { for (const header of Object.keys(handshakeResponse.headers)) { diff --git a/packages/backend/src/app/webhooks/webhook-service.ts b/packages/backend/src/app/webhooks/webhook-service.ts index faa9590ce..47a4b35d5 100644 --- a/packages/backend/src/app/webhooks/webhook-service.ts +++ b/packages/backend/src/app/webhooks/webhook-service.ts @@ -25,12 +25,13 @@ export const webhookService = { async handshake({ flow, payload, - }: CallbackParams): Promise { + simulate, + }: HandshakeParams): Promise { logger.info(`[WebhookService#handshake] flowId=${flow.id}`) const { projectId } = flow - - if (isNil(flow.publishedVersionId)) { + const flowVersionId = simulate ? (await flowVersionService.getFlowVersionOrThrow({ flowId: flow.id, versionId: undefined, removeSecrets: false })).id : flow.publishedVersionId + if (isNil(flowVersionId)) { logger.info( `[WebhookService#handshake] flowInstance not found, flowId=${flow.id}`, ) @@ -38,12 +39,12 @@ export const webhookService = { return null } - const flowVersion = await flowVersionService.getOneOrThrow(flow.publishedVersionId) + const flowVersion = await flowVersionService.getOneOrThrow(flowVersionId) const response = await triggerUtils.tryHandshake({ projectId, flowVersion, payload, - simulate: false, + simulate, }) if (response !== null) { logger.info(`[WebhookService#handshake] condition met, handshake executed, response: @@ -163,7 +164,7 @@ const getLatestFlowVersionOrThrow = async ( flowId: FlowId, projectId: ProjectId, ): Promise => { - const flowVersion = await flowVersionService.getFlowVersion({ + const flowVersion = await flowVersionService.getFlowVersionOrThrow({ flowId, versionId: undefined, }) @@ -208,6 +209,12 @@ type CallbackParams = { payload: EventPayload } +type HandshakeParams = { + flow: Flow + payload: EventPayload + simulate: boolean +} + type SyncParams = { flow: Flow diff --git a/packages/backend/src/app/workers/flow-worker/flow-worker.ts b/packages/backend/src/app/workers/flow-worker/flow-worker.ts index cf0bcca6a..5bb6b2d14 100755 --- a/packages/backend/src/app/workers/flow-worker/flow-worker.ts +++ b/packages/backend/src/app/workers/flow-worker/flow-worker.ts @@ -118,51 +118,53 @@ const loadInputAndLogFileId = async ({ flowVersion, flowRunId: jobData.runId, projectId: jobData.projectId, - triggerPayload: jobData.payload, - } - - if (jobData.executionType === ExecutionType.BEGIN) { - return { - input: { - executionType: ExecutionType.BEGIN, - ...baseInput, - }, - } } const flowRun = await flowRunService.getOneOrThrow({ id: jobData.runId, projectId: jobData.projectId, }) + switch (jobData.executionType) { + case ExecutionType.RESUME: { + if (isNil(flowRun.logsFileId)) { + throw new ActivepiecesError({ + code: ErrorCode.VALIDATION, + params: { + message: `flowRunId=${flowRun.id}`, + }, + }) + } + + const logFile = await fileService.getOneOrThrow({ + fileId: flowRun.logsFileId, + projectId: jobData.projectId, + }) - if (isNil(flowRun.pauseMetadata) || isNil(flowRun.logsFileId)) { - throw new ActivepiecesError({ - code: ErrorCode.VALIDATION, - params: { - message: `flowRunId=${flowRun.id}`, - }, - }) - } - - const logFile = await fileService.getOneOrThrow({ - fileId: flowRun.logsFileId, - projectId: jobData.projectId, - }) - - const serializedExecutionOutput = logFile.data.toString('utf-8') - const executionOutput: ExecutionOutput = JSON.parse( - serializedExecutionOutput, - ) - - - return { - input: { - ...baseInput, - executionType: ExecutionType.RESUME, - executionState: executionOutput.executionState, - resumePayload: jobData.payload, - }, - logFileId: logFile.id, + const serializedExecutionOutput = logFile.data.toString('utf-8') + const executionOutput: ExecutionOutput = JSON.parse( + serializedExecutionOutput, + ) + + + return { + input: { + ...baseInput, + executionType: ExecutionType.RESUME, + tasks: executionOutput.tasks, + executionState: executionOutput.executionState, + resumePayload: jobData.payload, + }, + logFileId: logFile.id, + } + } + case ExecutionType.BEGIN: + return { + input: { + triggerPayload: jobData.payload, + executionType: ExecutionType.BEGIN, + ...baseInput, + }, + } } } @@ -187,13 +189,6 @@ async function executeFlow(jobData: OneTimeJobData): Promise { await flowWorkerHooks.getHooks().preExecute({ projectId: jobData.projectId, runId: jobData.runId }) - const sandbox = await getSandbox({ - projectId: jobData.projectId, - flowVersion, - runEnvironment: jobData.environment, - }) - - logger.info(`[FlowWorker#executeFlow] flowRunId=${jobData.runId} sandboxId=${sandbox.boxId} prepareTime=${Date.now() - startTime}ms`) try { @@ -202,6 +197,14 @@ async function executeFlow(jobData: OneTimeJobData): Promise { jobData, }) + const sandbox = await getSandbox({ + projectId: jobData.projectId, + flowVersion, + runEnvironment: jobData.environment, + }) + + logger.info(`[FlowWorker#executeFlow] flowRunId=${jobData.runId} sandboxId=${sandbox.boxId} prepareTime=${Date.now() - startTime}ms`) + const { result: executionOutput } = await engineHelper.executeFlow( sandbox, input, diff --git a/packages/backend/src/app/workers/flow-worker/job-data.ts b/packages/backend/src/app/workers/flow-worker/job-data.ts index a5bb93343..aa6682d3f 100644 --- a/packages/backend/src/app/workers/flow-worker/job-data.ts +++ b/packages/backend/src/app/workers/flow-worker/job-data.ts @@ -1,4 +1,4 @@ -import { ExecutionType, FlowId, FlowRunId, FlowVersionId, ProjectId, RunEnvironment, TriggerType } from '@activepieces/shared' +import { ExecutionType, FlowId, FlowRetryPayload, FlowRunId, FlowVersionId, ProjectId, RunEnvironment, TriggerType } from '@activepieces/shared' export const LATEST_JOB_DATA_SCHEMA_VERSION = 3 @@ -32,6 +32,7 @@ export type OneTimeJobData = BaseJobData & { synchronousHandlerId?: string payload: unknown executionType: ExecutionType + retryPayload?: FlowRetryPayload } export type JobData = ScheduledJobData | OneTimeJobData \ No newline at end of file diff --git a/packages/backend/src/app/workers/sandbox/file-sandbox.ts b/packages/backend/src/app/workers/sandbox/file-sandbox.ts index bc76f4b2f..9af3e6393 100644 --- a/packages/backend/src/app/workers/sandbox/file-sandbox.ts +++ b/packages/backend/src/app/workers/sandbox/file-sandbox.ts @@ -1,4 +1,5 @@ import { rmdir, mkdir, readFile, writeFile, cp } from 'node:fs/promises' +import fs from 'fs-extra' import path from 'node:path' import { spawn } from 'node:child_process' import { AbstractSandbox, ExecuteSandboxResult, SandboxCtorParams } from './abstract-sandbox' @@ -34,8 +35,9 @@ export class FileSandbox extends AbstractSandbox { const command = [ `cd ${this.getSandboxFolderPath()}`, '&&', - `env -i AP_PIECES_SOURCE=${pieceSources} NODE_OPTIONS='--enable-source-maps'`, - AbstractSandbox.nodeExecutablePath, + `cross-env AP_PIECES_SOURCE=${pieceSources} NODE_OPTIONS=--enable-source-maps`, + '&&', + `"${AbstractSandbox.nodeExecutablePath}"`, 'main.js', operation, ].join(' ') @@ -58,14 +60,20 @@ export class FileSandbox extends AbstractSandbox { } public override getSandboxFolderPath(): string { - return path.join(__dirname, `../../sandbox/${this.boxId}`) + const systemCache = system.get(SystemProp.CACHE_PATH) ?? __dirname + return path.join(systemCache, 'sandbox', `${this.boxId}`) } protected override async setupCache(): Promise { logger.debug({ boxId: this.boxId, cacheKey: this._cacheKey, cachePath: this._cachePath }, '[FileSandbox#setupCache]') if (this._cachePath) { - await cp(this._cachePath, this.getSandboxFolderPath(), { recursive: true }) + if (process.platform === 'win32') { + await fs.copy(this._cachePath, this.getSandboxFolderPath()) + } + else { + await cp(this._cachePath, this.getSandboxFolderPath(), { recursive: true }) + } } } diff --git a/packages/backend/src/app/workers/sandbox/provisioner/sandbox-cache-key.ts b/packages/backend/src/app/workers/sandbox/provisioner/sandbox-cache-key.ts index 76c9b5e75..d754351ad 100644 --- a/packages/backend/src/app/workers/sandbox/provisioner/sandbox-cache-key.ts +++ b/packages/backend/src/app/workers/sandbox/provisioner/sandbox-cache-key.ts @@ -38,8 +38,8 @@ export const extractProvisionCacheKey = (params: ProvisionCacheInfo): string => -const extractCodeCacheKey = ({ sourceCodeHash }: CodeProvisionCacheInfo): string => { - return `CODE-sourceCodeHash-${sourceCodeHash}` +const extractCodeCacheKey = ({ sourceCodeHash, name, flowId }: CodeProvisionCacheInfo): string => { + return `CODE-sourceCodeHash-${sourceCodeHash}-name-${name}-flowId-${flowId}` } const extractFlowCacheKey = ({ flowVersionId }: FlowProvisionCacheInfo): string => { @@ -61,6 +61,8 @@ type BaseProvisionCacheInfo = { type CodeProvisionCacheInfo = BaseProvisionCacheInfo & { sourceCodeHash: string + name: string + flowId: string } type FlowProvisionCacheInfo = BaseProvisionCacheInfo & { diff --git a/packages/backend/src/assets/emails/invitation-email.html b/packages/backend/src/assets/emails/invitation-email.html index ddeaa91a7..30fd3463c 100644 --- a/packages/backend/src/assets/emails/invitation-email.html +++ b/packages/backend/src/assets/emails/invitation-email.html @@ -8,7 +8,7 @@ 96 - + @@ -16,29 +16,29 @@ - + - + Finish creating your account + +
- +
 ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ 
@@ -139,4 +154,4 @@ - \ No newline at end of file + diff --git a/packages/backend/src/assets/emails/reset-password.html b/packages/backend/src/assets/emails/reset-password.html index ef121d4a6..fedc1154c 100644 --- a/packages/backend/src/assets/emails/reset-password.html +++ b/packages/backend/src/assets/emails/reset-password.html @@ -302,7 +302,18 @@ + + + You can copy-paste the following link in your address bar if the above button doesn't work + + + + + {{setupLink}} + - \ No newline at end of file + diff --git a/packages/backend/src/assets/emails/verify-email.html b/packages/backend/src/assets/emails/verify-email.html index db1658e16..969634db1 100644 --- a/packages/backend/src/assets/emails/verify-email.html +++ b/packages/backend/src/assets/emails/verify-email.html @@ -294,6 +294,19 @@ + + + You can copy-paste the following link in your address bar if the above button doesn't work + + + + + + {{setupLink}} + + - \ No newline at end of file + diff --git a/packages/backend/src/main.ts b/packages/backend/src/main.ts index e122beefb..0b866d335 100644 --- a/packages/backend/src/main.ts +++ b/packages/backend/src/main.ts @@ -2,12 +2,11 @@ import { system } from './app/helper/system/system' import { SystemProp } from './app/helper/system/system-prop' import { databaseConnection } from './app/database/database-connection' import { logger } from './app/helper/logger' -import { ApEdition, ApEnvironment } from '@activepieces/shared' +import { ApEnvironment } from '@activepieces/shared' import { seedDevData } from './app/database/seeds/dev-seeds' import { setupApp } from './app/app' import { FastifyInstance } from 'fastify' -import { licenseValidator } from './app/ee/helper/license-validator' -import { getEdition } from './app/helper/secret-helper' +import { enforceLimits } from './app/ee/helper/license-validator' const start = async (app: FastifyInstance): Promise => { try { @@ -36,15 +35,8 @@ The application started on ${system.get(SystemProp.FRONTEND_URL)}, as specified logger.warn(`[WARNING]: The application is running in ${environemnt} mode.`) logger.warn(`[WARNING]: This is only shows pieces specified in AP_DEV_PIECES ${pieces} environment variable.`) } - - const edition = getEdition() - if (edition !== ApEdition.COMMUNITY) { - const verified = await licenseValidator.validate() - if (!verified) { - logger.error('[ERROR]: License key is not valid. Please contact sales@activepieces.com') - process.exit(1) - } - } + await enforceLimits() + } catch (err) { logger.error(err) diff --git a/packages/backend/test/helpers/mocks/index.ts b/packages/backend/test/helpers/mocks/index.ts index 7670554ca..cfcc52a72 100644 --- a/packages/backend/test/helpers/mocks/index.ts +++ b/packages/backend/test/helpers/mocks/index.ts @@ -1,5 +1,5 @@ -import { KeyAlgorithm, SigningKey, Platform, OAuthApp, FilteredPieceBehavior, CustomDomain, CustomDomainStatus, OtpModel, OtpType, OtpState, ProjectMember, ApiKey, ProjectMemberRole, ProjectMemberStatus } from '@activepieces/ee-shared' -import { UserStatus, User, apId, Project, NotificationStatus, ProjectType, PieceType, PackageType, Flow, FlowStatus, FlowVersion, TriggerType, FlowVersionState, FlowTemplate, TemplateType } from '@activepieces/shared' +import { KeyAlgorithm, SigningKey, Platform, OAuthApp, FilteredPieceBehavior, CustomDomain, CustomDomainStatus, OtpModel, OtpType, OtpState, ProjectMember, ApiKey, ProjectMemberRole, ProjectMemberStatus, GitRepo } from '@activepieces/ee-shared' +import { UserStatus, User, apId, Project, NotificationStatus, ProjectType, PieceType, PackageType, Flow, FlowStatus, FlowVersion, TriggerType, FlowVersionState, FlowTemplate, TemplateType, FlowRun, ExecutionOutputStatus, RunEnvironment } from '@activepieces/shared' import { faker } from '@faker-js/faker' import { PieceMetadataSchema } from '../../../src/app/pieces/piece-metadata-entity' import bcrypt from 'bcrypt' @@ -24,6 +24,7 @@ export const createMockUser = (user?: Partial): User => { status: user?.status ?? faker.helpers.enumValue(UserStatus), imageUrl: user?.imageUrl, title: user?.title, + verified: user?.verified ?? faker.datatype.boolean(), externalId: user?.externalId, platformId: user?.platformId ?? null, } @@ -72,18 +73,35 @@ export const createMockProject = (project?: Partial): Project => { } } +export const createMockGitRepo = (gitRepo?: Partial): GitRepo => { + return { + id: gitRepo?.id ?? apId(), + created: gitRepo?.created ?? faker.date.recent().toISOString(), + updated: gitRepo?.updated ?? faker.date.recent().toISOString(), + projectId: gitRepo?.projectId ?? apId(), + remoteUrl: gitRepo?.remoteUrl ?? `git@${faker.internet.url()}`, + sshPrivateKey: gitRepo?.sshPrivateKey ?? faker.internet.password(), + branch: gitRepo?.branch ?? faker.lorem.word(), + } +} + export const createMockPlatform = (platform?: Partial): Platform => { return { id: platform?.id ?? apId(), created: platform?.created ?? faker.date.recent().toISOString(), updated: platform?.updated ?? faker.date.recent().toISOString(), ownerId: platform?.ownerId ?? apId(), + enforceAllowedAuthDomains: platform?.enforceAllowedAuthDomains ?? false, + federatedAuthProviders: platform?.federatedAuthProviders ?? {}, + allowedAuthDomains: platform?.allowedAuthDomains ?? [], name: platform?.name ?? faker.lorem.word(), primaryColor: platform?.primaryColor ?? faker.color.rgb(), logoIconUrl: platform?.logoIconUrl ?? faker.image.urlPlaceholder(), fullLogoUrl: platform?.fullLogoUrl ?? faker.image.urlPlaceholder(), + emailAuthEnabled: platform?.emailAuthEnabled ?? true, favIconUrl: platform?.favIconUrl ?? faker.image.urlPlaceholder(), filteredPieceNames: platform?.filteredPieceNames ?? [], + ssoEnabled: platform?.ssoEnabled ?? faker.datatype.boolean(), filteredPieceBehavior: platform?.filteredPieceBehavior ?? faker.helpers.enumValue(FilteredPieceBehavior), smtpHost: platform?.smtpHost ?? faker.internet.domainName(), smtpPort: platform?.smtpPort ?? faker.internet.port(), @@ -92,6 +110,7 @@ export const createMockPlatform = (platform?: Partial): Platform => { smtpUseSSL: platform?.smtpUseSSL ?? faker.datatype.boolean(), smtpSenderEmail: platform?.smtpSenderEmail ?? faker.internet.email(), privacyPolicyUrl: platform?.privacyPolicyUrl ?? faker.internet.url(), + gitSyncEnabled: platform?.gitSyncEnabled ?? faker.datatype.boolean(), termsOfServiceUrl: platform?.termsOfServiceUrl ?? faker.internet.url(), embeddingEnabled: platform?.embeddingEnabled ?? faker.datatype.boolean(), cloudAuthEnabled: platform?.cloudAuthEnabled ?? faker.datatype.boolean(), @@ -257,6 +276,25 @@ export const createMockOtp = (otp?: Partial): OtpModel => { } } +export const createMockFlowRun = (flowRun?: Partial): FlowRun => { + return { + id: flowRun?.id ?? apId(), + created: flowRun?.created ?? faker.date.recent().toISOString(), + updated: flowRun?.updated ?? faker.date.recent().toISOString(), + projectId: flowRun?.projectId ?? apId(), + flowId: flowRun?.flowId ?? apId(), + tags: flowRun?.tags ?? [], + flowVersionId: flowRun?.flowVersionId ?? apId(), + flowDisplayName: flowRun?.flowDisplayName ?? faker.lorem.word(), + logsFileId: flowRun?.logsFileId ?? null, + tasks: flowRun?.tasks, + status: flowRun?.status ?? faker.helpers.enumValue(ExecutionOutputStatus), + startTime: flowRun?.startTime ?? faker.date.recent().toISOString(), + finishTime: flowRun?.finishTime ?? faker.date.recent().toISOString(), + environment: flowRun?.environment ?? faker.helpers.enumValue(RunEnvironment), + } +} + export const createMockFlow = (flow?: Partial): Flow => { return { id: flow?.id ?? apId(), @@ -287,7 +325,7 @@ export const createMockFlowVersion = (flowVersion?: Partial): FlowV flowId: flowVersion?.flowId ?? apId(), trigger: flowVersion?.trigger ?? emptyTrigger, state: flowVersion?.state ?? faker.helpers.enumValue(FlowVersionState), - updatedBy: flowVersion?.updatedBy ?? apId(), + updatedBy: flowVersion?.updatedBy, valid: flowVersion?.valid ?? faker.datatype.boolean(), } } diff --git a/packages/backend/test/integration/ce/authentication/authentication.test.ts b/packages/backend/test/integration/ce/authentication/authentication.test.ts index 8938e79f7..7a892fe3f 100644 --- a/packages/backend/test/integration/ce/authentication/authentication.test.ts +++ b/packages/backend/test/integration/ce/authentication/authentication.test.ts @@ -40,17 +40,18 @@ describe('Authentication API', () => { const responseBody = response?.json() expect(response?.statusCode).toBe(StatusCodes.OK) - expect(Object.keys(responseBody)).toHaveLength(15) + expect(Object.keys(responseBody)).toHaveLength(16) expect(responseBody?.id).toHaveLength(21) expect(responseBody?.created).toBeDefined() expect(responseBody?.updated).toBeDefined() + expect(responseBody?.verified).toBe(true) expect(responseBody?.email).toBe(mockSignUpRequest.email) expect(responseBody?.firstName).toBe(mockSignUpRequest.firstName) expect(responseBody?.lastName).toBe(mockSignUpRequest.lastName) expect(responseBody?.trackEvents).toBe(mockSignUpRequest.trackEvents) expect(responseBody?.newsLetter).toBe(mockSignUpRequest.newsLetter) expect(responseBody?.password).toBeUndefined() - expect(responseBody?.status).toBe('VERIFIED') + expect(responseBody?.status).toBe('ACTIVE') expect(responseBody?.platformId).toBe(null) expect(responseBody?.externalId).toBe(null) expect(responseBody?.projectId).toHaveLength(21) @@ -112,7 +113,8 @@ describe('Authentication API', () => { const mockUser = createMockUser({ email: mockEmail, password: mockPassword, - status: UserStatus.VERIFIED, + verified: true, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) @@ -137,7 +139,7 @@ describe('Authentication API', () => { const responseBody = response?.json() expect(response?.statusCode).toBe(StatusCodes.OK) - expect(Object.keys(responseBody)).toHaveLength(15) + expect(Object.keys(responseBody)).toHaveLength(16) expect(responseBody?.id).toBe(mockUser.id) expect(responseBody?.email).toBe(mockEmail) expect(responseBody?.firstName).toBe(mockUser.firstName) @@ -146,6 +148,7 @@ describe('Authentication API', () => { expect(responseBody?.newsLetter).toBe(mockUser.newsLetter) expect(responseBody?.password).toBeUndefined() expect(responseBody?.status).toBe(mockUser.status) + expect(responseBody?.verified).toBe(mockUser.verified) expect(responseBody?.platformId).toBe(null) expect(responseBody?.externalId).toBe(null) expect(responseBody?.projectId).toBe(mockProject.id) @@ -160,7 +163,8 @@ describe('Authentication API', () => { const mockUser = createMockUser({ email: mockEmail, password: mockPassword, - status: UserStatus.VERIFIED, + verified: true, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) diff --git a/packages/backend/test/integration/ce/flows/flow-worker.test.ts b/packages/backend/test/integration/ce/flows/flow-worker.test.ts new file mode 100644 index 000000000..38c3fac72 --- /dev/null +++ b/packages/backend/test/integration/ce/flows/flow-worker.test.ts @@ -0,0 +1,155 @@ +import { ActionType, ExecutionOutputStatus, ExecutionType, FlowStatus, FlowVersionState, RunEnvironment, TriggerType } from '@activepieces/shared' +import { FastifyInstance } from 'fastify' +import { databaseConnection } from '../../../../src/app/database/database-connection' +import { setupApp } from '../../../../src/app/app' +import { createMockFlow, createMockFlowRun, createMockFlowVersion, createMockProject, createMockUser } from '../../../helpers/mocks' +import { flowWorker } from '../../../../src/app/workers/flow-worker/flow-worker' +import { fileCompressor } from '../../../../src/app/file/utils/file-compressor' + +let app: FastifyInstance | null = null + +beforeAll(async () => { + await databaseConnection.initialize() + app = await setupApp() +}) + +afterAll(async () => { + await databaseConnection.destroy() + await app?.close() +}) + +describe('flow execution', () => { + + it('should execute simple flow with code and data mapper', async () => { + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save([mockUser]) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save([mockProject]) + + const mockFlow = createMockFlow({ projectId: mockProject.id, status: FlowStatus.ENABLED }) + await databaseConnection.getRepository('flow').save([mockFlow]) + + const mockFlowVersion = createMockFlowVersion({ + flowId: mockFlow.id, + updatedBy: mockUser.id, + state: FlowVersionState.LOCKED, + trigger: { + type: TriggerType.WEBHOOK, + settings: { + inputUiInfo: {}, + }, + valid: true, + name: 'webhook', + displayName: 'Webhook', + nextAction: { + name: 'echo_step', + displayName: 'Echo Step', + type: ActionType.CODE, + settings: { + inputUiInfo: {}, + input: { + 'key': '{{ 1 + 2 }}', + }, + sourceCode: { + packageJson: '{}', + code: ` + export const code = async (inputs) => { + return inputs; + }; + `, + }, + }, + nextAction: { + name: 'datamapper', + diplayName: 'Datamapper', + type: ActionType.PIECE, + settings: { + inputUiInfo: {}, + pieceName: '@activepieces/piece-data-mapper', + pieceVersion: '0.3.0', + packageType: 'REGISTRY', + pieceType: 'OFFICIAL', + actionName: 'advanced_mapping', + input: { + mapping: { + 'key': '{{ 1 + 2 }}', + }, + }, + }, + valid: true, + }, + valid: true, + }, + }, + }) + await databaseConnection.getRepository('flow_version').save([mockFlowVersion]) + + + const mockFlowRun = createMockFlowRun({ + flowVersionId: mockFlowVersion.id, + projectId: mockProject.id, + flowId: mockFlow.id, + status: ExecutionOutputStatus.RUNNING, + }) + await databaseConnection.getRepository('flow_run').save([mockFlowRun]) + + await flowWorker.executeFlow({ + flowVersionId: mockFlowVersion.id, + projectId: mockProject.id, + environment: RunEnvironment.PRODUCTION, + runId: mockFlowRun.id, + payload: { + + }, + executionType: ExecutionType.BEGIN, + }) + + const flowRun = await databaseConnection.getRepository('flow_run').findOneByOrFail({ + id: mockFlowRun.id, + }) + expect(flowRun.status).toEqual(ExecutionOutputStatus.SUCCEEDED) + + const file = await databaseConnection.getRepository('file').findOneByOrFail({ + id: flowRun.logsFileId, + }) + const decompressedData = await fileCompressor.decompress({ + data: file.data, + compression: file.compression, + }) + expect(JSON.parse(decompressedData.toString('utf-8')).executionState).toEqual( { + 'steps': { + 'webhook': { + 'type': 'WEBHOOK', + 'status': 'SUCCEEDED', + 'input': {}, + 'output': {}, + }, + 'echo_step': { + 'type': 'CODE', + 'status': 'SUCCEEDED', + 'input': { + 'key': 3, + }, + 'output': { + 'key': 3, + }, + }, + 'datamapper': { + 'type': 'PIECE', + 'status': 'SUCCEEDED', + 'input': { + 'mapping': { + 'key': 3, + }, + }, + 'output': { + 'key': 3, + }, + }, + }, + }) + + }, 60000) + +}) \ No newline at end of file diff --git a/packages/backend/test/integration/ce/flows/flow.test.ts b/packages/backend/test/integration/ce/flows/flow.test.ts index 401a5f58c..2baeb5d8e 100644 --- a/packages/backend/test/integration/ce/flows/flow.test.ts +++ b/packages/backend/test/integration/ce/flows/flow.test.ts @@ -20,6 +20,7 @@ afterAll(async () => { describe('Flow API', () => { describe('Create Flow endpoint', () => { + it('Adds an empty flow', async () => { // arrange const mockUser = createMockUser() @@ -32,12 +33,16 @@ describe('Flow API', () => { const mockCreateFlowRequest = { displayName: 'test flow', + projectId: mockProject.id, } // act const response = await app?.inject({ method: 'POST', url: '/v1/flows', + query: { + projectId: mockProject.id, + }, headers: { authorization: `Bearer ${mockToken}`, }, @@ -235,4 +240,116 @@ describe('Flow API', () => { expect(responseBody?.version?.state).toBe('LOCKED') }) }) + + describe('List Flows endpoint', () => { + it('Filters Flows by status', async () => { + // arrange + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save([mockUser]) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save([mockProject]) + + const mockEnabledFlow = createMockFlow({ projectId: mockProject.id, status: FlowStatus.ENABLED }) + const mockDisabledFlow = createMockFlow({ projectId: mockProject.id, status: FlowStatus.DISABLED }) + await databaseConnection.getRepository('flow').save([mockEnabledFlow, mockDisabledFlow]) + + const mockEnabledFlowVersion = createMockFlowVersion({ flowId: mockEnabledFlow.id }) + const mockDisabledFlowVersion = createMockFlowVersion({ flowId: mockDisabledFlow.id }) + await databaseConnection.getRepository('flow_version').save([mockEnabledFlowVersion, mockDisabledFlowVersion]) + + const mockToken = await generateMockToken({ type: PrincipalType.USER, projectId: mockProject.id }) + + // act + const response = await app?.inject({ + method: 'GET', + url: '/v1/flows', + query: { + projectId: mockProject.id, + status: 'ENABLED', + }, + headers: { + authorization: `Bearer ${mockToken}`, + }, + }) + + // assert + expect(response?.statusCode).toBe(StatusCodes.OK) + const responseBody = response?.json() + + expect(responseBody.data).toHaveLength(1) + expect(responseBody.data[0].id).toBe(mockEnabledFlow.id) + }) + + it('Populates Flow version', async () => { + // arrange + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save([mockUser]) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save([mockProject]) + + const mockFlow = createMockFlow({ projectId: mockProject.id }) + await databaseConnection.getRepository('flow').save([mockFlow]) + + const mockFlowVersion = createMockFlowVersion({ flowId: mockFlow.id }) + await databaseConnection.getRepository('flow_version').save([mockFlowVersion]) + + const mockToken = await generateMockToken({ type: PrincipalType.USER, projectId: mockProject.id }) + + // act + const response = await app?.inject({ + method: 'GET', + url: '/v1/flows', + query: { + projectId: mockProject.id, + }, + headers: { + authorization: `Bearer ${mockToken}`, + }, + }) + + // assert + expect(response?.statusCode).toBe(StatusCodes.OK) + const responseBody = response?.json() + + expect(responseBody?.data).toHaveLength(1) + expect(responseBody?.data?.[0]?.id).toBe(mockFlow.id) + expect(responseBody?.data?.[0]?.version?.id).toBe(mockFlowVersion.id) + }) + + it('Fails if a flow with no version exists', async () => { + // arrange + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save([mockUser]) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save([mockProject]) + + const mockFlow = createMockFlow({ projectId: mockProject.id }) + await databaseConnection.getRepository('flow').save([mockFlow]) + + const mockToken = await generateMockToken({ type: PrincipalType.USER, projectId: mockProject.id }) + + // act + const response = await app?.inject({ + method: 'GET', + url: '/v1/flows', + query: { + projectId: mockProject.id, + }, + headers: { + authorization: `Bearer ${mockToken}`, + }, + }) + + // assert + expect(response?.statusCode).toBe(StatusCodes.NOT_FOUND) + const responseBody = response?.json() + + expect(responseBody?.code).toBe('ENTITY_NOT_FOUND') + expect(responseBody?.params?.entityType).toBe('FlowVersion') + expect(responseBody?.params?.message).toBe(`flowId=${mockFlow.id}`) + }) + }) }) diff --git a/packages/backend/test/integration/ce/flows/project-worker.test.ts b/packages/backend/test/integration/ce/flows/project-worker.test.ts new file mode 100644 index 000000000..558727d39 --- /dev/null +++ b/packages/backend/test/integration/ce/flows/project-worker.test.ts @@ -0,0 +1,50 @@ +import { databaseConnection } from '../../../../src/app/database/database-connection' +import { setupApp } from '../../../../src/app/app' +import { generateMockToken } from '../../../helpers/auth' +import { createMockUser, createMockProject } from '../../../helpers/mocks' +import { StatusCodes } from 'http-status-codes' +import { FastifyInstance } from 'fastify' +import { PrincipalType } from '@activepieces/shared' + +let app: FastifyInstance | null = null + +beforeAll(async () => { + await databaseConnection.initialize() + app = await setupApp() +}) + +afterAll(async () => { + await databaseConnection.destroy() + await app?.close() +}) + +describe('Project Worker API', () => { + describe('Get worker project endpoint', () => { + it('Returns worker project', async () => { + // arrange + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save([mockUser]) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save([mockProject]) + + const mockToken = await generateMockToken({ type: PrincipalType.WORKER, projectId: mockProject.id }) + + // act + const response = await app?.inject({ + method: 'GET', + url: '/v1/worker/project', + headers: { + authorization: `Bearer ${mockToken}`, + }, + }) + + // assert + expect(response?.statusCode).toBe(StatusCodes.OK) + const responseBody = response?.json() + + expect(Object.keys(responseBody)).toHaveLength(9) + expect(responseBody?.id).toBe(mockProject.id) + }) + }) +}) diff --git a/packages/backend/test/integration/cloud/app-sumo/app-sumo.test.ts b/packages/backend/test/integration/cloud/app-sumo/app-sumo.test.ts new file mode 100644 index 000000000..e175f6033 --- /dev/null +++ b/packages/backend/test/integration/cloud/app-sumo/app-sumo.test.ts @@ -0,0 +1,51 @@ +import { databaseConnection } from '../../../../src/app/database/database-connection' +import { setupApp } from '../../../../src/app/app' +import { StatusCodes } from 'http-status-codes' +import { FastifyInstance } from 'fastify' + +let app: FastifyInstance | null = null + +beforeAll(async () => { + await databaseConnection.initialize() + app = await setupApp() +}) + +afterAll(async () => { + await databaseConnection.destroy() + await app?.close() +}) + +describe('AppSumo API', () => { + describe('Action endpoint', () => { + it('Activates new accounts', async () => { + // arrange + const mockEmail = 'mock-email' + + const requestBody = { + action: 'activate', + plan_id: 'plan_id', + uuid: 'uuid', + activation_email: mockEmail, + } + + const appSumoToken = 'app-sumo-token' + + // act + const response = await app?.inject({ + method: 'POST', + url: '/v1/appsumo/action', + headers: { + authorization: `Bearer ${appSumoToken}`, + }, + body: requestBody, + }) + + // assert + expect(response?.statusCode).toBe(StatusCodes.CREATED) + const responseBody = response?.json() + + expect(responseBody?.message).toBe('success') + expect(responseBody?.redirect_url).toBe(`https://cloud.activepieces.com/sign-up?email=${mockEmail}`) + }) + }) +}) diff --git a/packages/backend/test/integration/cloud/authn/cloud-authn.test.ts b/packages/backend/test/integration/cloud/authn/cloud-authn.test.ts index fc3b33656..a1e9c5467 100644 --- a/packages/backend/test/integration/cloud/authn/cloud-authn.test.ts +++ b/packages/backend/test/integration/cloud/authn/cloud-authn.test.ts @@ -21,7 +21,7 @@ beforeAll(async () => { beforeEach(async () => { emailService.sendOtpEmail = jest.fn() stripeHelper.getOrCreateCustomer = jest.fn().mockResolvedValue(faker.string.alphanumeric()) - + await databaseConnection.getRepository('flag').delete({}) }) @@ -32,6 +32,69 @@ afterAll(async () => { describe('Authentication API', () => { describe('Sign up Endpoint', () => { + + it('Add new user if the domain is allowed', async () => { + // arrange + const { mockPlatform, mockUser, mockCustomDomain } = await createMockPlatformAndDomain() + const mockSignUpRequest = createMockSignUpRequest() + await databaseConnection.getRepository('platform').update(mockPlatform.id, { + enforceAllowedAuthDomains: true, + allowedAuthDomains: [mockSignUpRequest.email.split('@')[1]], + }) + + const mockProject = createMockProject({ ownerId: mockUser.id, platformId: mockPlatform.id }) + await databaseConnection.getRepository('project').save(mockProject) + + const mockProjectMember = createProjectMember({ + projectId: mockProject.id, + email: mockSignUpRequest.email, + platformId: mockPlatform.id, + status: ProjectMemberStatus.ACTIVE, + role: ProjectMemberRole.ADMIN, + }) + await databaseConnection.getRepository('project_member').save(mockProjectMember) + + // act + const response = await app?.inject({ + method: 'POST', + url: '/v1/authentication/sign-up', + body: mockSignUpRequest, + headers: { + Host: mockCustomDomain.domain, + }, + }) + + // assert + // assert + expect(response?.statusCode).toBe(StatusCodes.OK) + }) + it('Fails If the domain is not allowed', async () => { + // arrange + const { mockPlatform, mockCustomDomain } = await createMockPlatformAndDomain() + await databaseConnection.getRepository('platform').update(mockPlatform.id, { + enforceAllowedAuthDomains: true, + allowedAuthDomains: [], + ssoEnabled: true, + }) + const mockSignUpRequest = createMockSignUpRequest() + + // act + // act + const response = await app?.inject({ + method: 'POST', + url: '/v1/authentication/sign-up', + body: mockSignUpRequest, + headers: { + Host: mockCustomDomain.domain, + }, + }) + // assert + // assert + expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) + const responseBody = response?.json() + expect(responseBody?.code).toBe('DOMAIN_NOT_ALLOWED') + }) + it('Adds new user', async () => { const { mockPlatform, mockCustomDomain } = await createMockPlatformAndDomain(CLOUD_PLATFORM_ID) @@ -52,7 +115,7 @@ describe('Authentication API', () => { expect(response?.statusCode).toBe(StatusCodes.OK) const responseBody = response?.json() - expect(Object.keys(responseBody)).toHaveLength(15) + expect(Object.keys(responseBody)).toHaveLength(16) expect(responseBody?.id).toHaveLength(21) expect(responseBody?.created).toBeDefined() expect(responseBody?.updated).toBeDefined() @@ -62,13 +125,14 @@ describe('Authentication API', () => { expect(responseBody?.trackEvents).toBe(mockSignUpRequest.trackEvents) expect(responseBody?.newsLetter).toBe(mockSignUpRequest.newsLetter) expect(responseBody?.password).toBeUndefined() - expect(responseBody?.status).toBe('CREATED') + expect(responseBody?.status).toBe('ACTIVE') + expect(responseBody?.verified).toBe(false) expect(responseBody?.platformId).toBe(mockPlatform.id) expect(responseBody?.externalId).toBe(null) expect(responseBody?.projectId).toHaveLength(21) expect(responseBody?.token).toBeDefined() }) - + it('Sends a verification email', async () => { // arrange const mockSignUpRequest = createMockSignUpRequest() @@ -133,9 +197,10 @@ describe('Authentication API', () => { const responseBody = response?.json() expect(responseBody?.platformId).toBe(mockPlatform.id) - expect(responseBody?.status).toBe('VERIFIED') + expect(responseBody?.status).toBe('ACTIVE') + expect(responseBody?.verified).toBe(true) expect(responseBody?.projectId).toBe(mockProject.id) - }) + }) it('fails to sign up invited user platform if no project exist', async () => { // arrange @@ -158,9 +223,9 @@ describe('Authentication API', () => { expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) const responseBody = response?.json() - expect(responseBody?.code).toBe('INVITATIION_ONLY_SIGN_UP') + expect(responseBody?.code).toBe('INVITATION_ONLY_SIGN_UP') }) - + it('Adds tasks for referrals', async () => { // arrange const { mockCustomDomain, mockPlatform } = await createMockPlatformAndDomain(CLOUD_PLATFORM_ID) @@ -202,7 +267,7 @@ describe('Authentication API', () => { expect(referredUserPlan?.tasks).toBe(1500) }) - + it('Fails if USER_CREATED flag is set, and sign-up is disabled', async () => { // arrange const mockSignUpRequest = createMockSignUpRequest() @@ -251,11 +316,108 @@ describe('Authentication API', () => { expect(project?.displayName).toBe(`${responseBody.firstName}'s Project`) expect(project?.type).toBe(ProjectType.PLATFORM_MANAGED) expect(project?.platformId).toBe(mockPlatform.id) - }) + }) }) describe('Sign in Endpoint', () => { + + it('Fails If the email auth is not enabled', async () => { + // arrange + const mockPlatformId = faker.string.nanoid() + const mockPlatformDomain = faker.internet.domainName() + + const rawPassword = faker.internet.password() + const mockUser = createMockUser({ + email: faker.internet.email(), + password: rawPassword, + verified: true, + status: UserStatus.ACTIVE, + platformId: mockPlatformId, + }) + await databaseConnection.getRepository('user').save(mockUser) + + const mockPlatform = createMockPlatform({ id: mockPlatformId, ownerId: mockUser.id, emailAuthEnabled: false, ssoEnabled: true }) + await databaseConnection.getRepository('platform').save(mockPlatform) + + const mockCustomDomain = createMockCustomDomain({ platformId: mockPlatformId, domain: mockPlatformDomain }) + await databaseConnection.getRepository('custom_domain').save(mockCustomDomain) + + const mockProject = createMockProject({ + ownerId: mockUser.id, + platformId: mockPlatformId, + }) + await databaseConnection.getRepository('project').save(mockProject) + + + const mockSignInRequest = createMockSignInRequest({ + email: mockUser.email, + password: rawPassword, + }) + + // act + const response = await app?.inject({ + method: 'POST', + url: '/v1/authentication/sign-in', + headers: { + Host: mockPlatformDomain, + }, + body: mockSignInRequest, + }) + expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) + const responseBody = response?.json() + + expect(responseBody?.code).toBe('EMAIL_AUTH_DISABLED') + }) + + it('Fails If the domain is not allowed', async () => { + // arrange + const mockPlatformId = faker.string.nanoid() + const mockPlatformDomain = faker.internet.domainName() + + const rawPassword = faker.internet.password() + const mockUser = createMockUser({ + email: faker.internet.email(), + password: rawPassword, + verified: true, + status: UserStatus.ACTIVE, + platformId: mockPlatformId, + }) + await databaseConnection.getRepository('user').save(mockUser) + + const mockPlatform = createMockPlatform({ id: mockPlatformId, ownerId: mockUser.id, allowedAuthDomains: [mockPlatformDomain], enforceAllowedAuthDomains: true, ssoEnabled: true }) + await databaseConnection.getRepository('platform').save(mockPlatform) + + const mockCustomDomain = createMockCustomDomain({ platformId: mockPlatformId, domain: mockPlatformDomain }) + await databaseConnection.getRepository('custom_domain').save(mockCustomDomain) + + const mockProject = createMockProject({ + ownerId: mockUser.id, + platformId: mockPlatformId, + }) + await databaseConnection.getRepository('project').save(mockProject) + + + const mockSignInRequest = createMockSignInRequest({ + email: mockUser.email, + password: rawPassword, + }) + + // act + const response = await app?.inject({ + method: 'POST', + url: '/v1/authentication/sign-in', + headers: { + Host: mockPlatformDomain, + }, + body: mockSignInRequest, + }) + expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) + const responseBody = response?.json() + + expect(responseBody?.code).toBe('DOMAIN_NOT_ALLOWED') + }) + it('Logs in existing users', async () => { // arrange const mockEmail = faker.internet.email() @@ -264,7 +426,8 @@ describe('Authentication API', () => { const mockUser = createMockUser({ email: mockEmail, password: mockPassword, - status: UserStatus.VERIFIED, + verified: true, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) @@ -289,7 +452,7 @@ describe('Authentication API', () => { const responseBody = response?.json() expect(response?.statusCode).toBe(StatusCodes.OK) - expect(Object.keys(responseBody)).toHaveLength(15) + expect(Object.keys(responseBody)).toHaveLength(16) expect(responseBody?.id).toBe(mockUser.id) expect(responseBody?.email).toBe(mockEmail) expect(responseBody?.firstName).toBe(mockUser.firstName) @@ -298,6 +461,7 @@ describe('Authentication API', () => { expect(responseBody?.newsLetter).toBe(mockUser.newsLetter) expect(responseBody?.password).toBeUndefined() expect(responseBody?.status).toBe(mockUser.status) + expect(responseBody?.verified).toBe(mockUser.verified) expect(responseBody?.platformId).toBe(null) expect(responseBody?.externalId).toBe(null) expect(responseBody?.projectId).toBe(mockProject.id) @@ -314,7 +478,8 @@ describe('Authentication API', () => { const mockUser = createMockUser({ email: mockEmail, password: mockPassword, - status: UserStatus.VERIFIED, + verified: true, + status: UserStatus.ACTIVE, platformId: mockPlatformId, }) await databaseConnection.getRepository('user').save(mockUser) @@ -366,7 +531,8 @@ describe('Authentication API', () => { const mockUser = createMockUser({ email: mockEmail, password: mockPassword, - status: UserStatus.VERIFIED, + verified: true, + status: UserStatus.ACTIVE, platformId: mockPlatformId, }) await databaseConnection.getRepository('user').save(mockUser) @@ -409,7 +575,8 @@ describe('Authentication API', () => { const mockUser = createMockUser({ email: mockEmail, password: mockPassword, - status: UserStatus.VERIFIED, + verified: true, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) @@ -436,7 +603,7 @@ describe('Authentication API', () => { expect(responseBody?.code).toBe('INVALID_CREDENTIALS') }) - it('Fails if user status is SUSPENDED', async () => { + it('Fails if user status is INACTIVE', async () => { // arrange const mockEmail = faker.internet.email() const mockPassword = 'password' @@ -444,7 +611,8 @@ describe('Authentication API', () => { const mockUser = createMockUser({ email: mockEmail, password: mockPassword, - status: UserStatus.SUSPENDED, + verified: true, + status: UserStatus.INACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) @@ -469,7 +637,7 @@ describe('Authentication API', () => { expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) const responseBody = response?.json() - expect(responseBody?.code).toBe('EMAIL_IS_NOT_VERIFIED') + expect(responseBody?.code).toBe('USER_IS_INACTIVE') expect(responseBody?.params?.email).toBe(mockUser.email) }) }) diff --git a/packages/backend/test/integration/cloud/authn/enterprise-local-authn.test.ts b/packages/backend/test/integration/cloud/authn/enterprise-local-authn.test.ts index e3d900d12..2ffe450af 100644 --- a/packages/backend/test/integration/cloud/authn/enterprise-local-authn.test.ts +++ b/packages/backend/test/integration/cloud/authn/enterprise-local-authn.test.ts @@ -23,7 +23,8 @@ describe('Enterprise Local Authn API', () => { describe('Verify Email Endpoint', () => { it('Verifies user', async () => { const mockUser = createMockUser({ - status: UserStatus.CREATED, + verified: false, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) @@ -51,15 +52,16 @@ describe('Enterprise Local Authn API', () => { expect(response?.body).toBe('') const user = await databaseConnection.getRepository('user').findOneBy({ id: mockUser.id }) - expect(user?.status).toBe(UserStatus.VERIFIED) - + expect(user?.status).toBe(UserStatus.ACTIVE) + expect(user?.verified).toBe(true) const otp = await databaseConnection.getRepository('otp').findOneBy({ id: mockOtp.id }) expect(otp?.state).toBe(OtpState.CONFIRMED) }) it('Fails if OTP is wrong', async () => { const mockUser = createMockUser({ - status: UserStatus.CREATED, + verified: false, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) @@ -91,12 +93,15 @@ describe('Enterprise Local Authn API', () => { expect(responseBody?.code).toBe('INVALID_OTP') const user = await databaseConnection.getRepository('user').findOneBy({ id: mockUser.id }) - expect(user?.status).toBe(UserStatus.CREATED) + expect(user?.status).toBe(UserStatus.ACTIVE) + expect(user?.verified).toBe(false) + }) it('Fails if OTP has expired', async () => { const mockUser = createMockUser({ - status: UserStatus.CREATED, + verified: false, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) @@ -126,12 +131,15 @@ describe('Enterprise Local Authn API', () => { expect(responseBody?.code).toBe('INVALID_OTP') const user = await databaseConnection.getRepository('user').findOneBy({ id: mockUser.id }) - expect(user?.status).toBe(UserStatus.CREATED) + expect(user?.status).toBe(UserStatus.ACTIVE) + expect(user?.verified).toBe(false) + }) it('Fails if OTP was confirmed before', async () => { const mockUser = createMockUser({ - status: UserStatus.CREATED, + verified: false, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save(mockUser) @@ -160,7 +168,7 @@ describe('Enterprise Local Authn API', () => { expect(responseBody?.code).toBe('INVALID_OTP') const user = await databaseConnection.getRepository('user').findOneBy({ id: mockUser.id }) - expect(user?.status).toBe(UserStatus.CREATED) + expect(user?.verified).toBe(false) }) }) diff --git a/packages/backend/test/integration/cloud/core/security.test.ts b/packages/backend/test/integration/cloud/core/security.test.ts new file mode 100644 index 000000000..f81b2b8be --- /dev/null +++ b/packages/backend/test/integration/cloud/core/security.test.ts @@ -0,0 +1,594 @@ +import { FastifyInstance, FastifyRequest } from 'fastify' +import { databaseConnection } from '../../../../src/app/database/database-connection' +import { setupApp } from '../../../../src/app/app' +import { securityHandlerChain } from '../../../../src/app/core/security/security-handler-chain' +import { ActivepiecesError, EndpointScope, ErrorCode, PlatformRole, Principal, PrincipalType, ProjectType, apId } from '@activepieces/shared' +import { createMockFlow, createMockPlatformWithOwner, createMockProject, setupMockApiKeyServiceAccount } from '../../../helpers/mocks' +import { generateMockToken } from '../../../helpers/auth' + +let app: FastifyInstance | null = null + +beforeAll(async () => { + await databaseConnection.initialize() + app = await setupApp() +}) + +afterAll(async () => { + await databaseConnection.destroy() + await app?.close() +}) + +describe('API Security', () => { + describe('Global API Key Authentication', () => { + it('Authenticates Admin User using Global API Key', async () => { + // arrange + const mockApiKey = 'api-key' + const mockRequest = { + method: 'POST', + routerPath: '/v1/admin/platforms', + headers: { + 'api-key': mockApiKey, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SUPER_USER], + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).resolves.toBeUndefined() + + expect(mockRequest.principal).toEqual(expect.objectContaining({ + id: expect.stringMatching(/SUPER_USER_.{21}/), + type: PrincipalType.SUPER_USER, + projectId: expect.stringMatching(/SUPER_USER_.{21}/), + })) + }) + + it('Fails if provided API key is invalid', async () => { + // arrange + const mockInvalidApiKey = '321' + const mockRequest = { + method: 'POST', + routerPath: '/v1/admin/users', + headers: { + 'api-key': mockInvalidApiKey, + }, + routeConfig: {}, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + return result.catch(e => { + expect(e).toEqual(new ActivepiecesError({ + code: ErrorCode.INVALID_API_KEY, + params: {}, + })) + }) + }) + }) + + describe('Platform API Key Authentication', () => { + it('Authenticates service principals', async () => { + // arrange + const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() + + await databaseConnection.getRepository('user').save([mockOwner]) + await databaseConnection.getRepository('platform').save([mockPlatform]) + await databaseConnection.getRepository('api_key').save([mockApiKey]) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockApiKey.value}`, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + scope: EndpointScope.PLATFORM, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).resolves.toBeUndefined() + + expect(mockRequest.principal).toEqual(expect.objectContaining({ + id: mockApiKey.id, + type: PrincipalType.SERVICE, + projectId: expect.stringMatching(/ANONYMOUS_.{21}/), + projectType: ProjectType.PLATFORM_MANAGED, + platform: { + id: mockPlatform.id, + role: PlatformRole.OWNER, + }, + })) + }) + + it('Gets projectId from body if endpoint scope is PROJECT', async () => { + // arrange + const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() + const mockProject = createMockProject({ ownerId: mockOwner.id, platformId: mockPlatform.id }) + + await databaseConnection.getRepository('user').save([mockOwner]) + await databaseConnection.getRepository('platform').save([mockPlatform]) + await databaseConnection.getRepository('api_key').save([mockApiKey]) + await databaseConnection.getRepository('project').save([mockProject]) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockApiKey.value}`, + }, + body: { + projectId: mockProject.id, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + scope: EndpointScope.PROJECT, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).resolves.toBeUndefined() + + expect(mockRequest.principal).toEqual(expect.objectContaining({ + id: mockApiKey.id, + type: PrincipalType.SERVICE, + projectId: mockProject.id, + projectType: ProjectType.PLATFORM_MANAGED, + platform: { + id: mockPlatform.id, + role: PlatformRole.OWNER, + }, + })) + }) + + it('Gets projectId from query if endpoint scope is PROJECT', async () => { + // arrange + const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() + const mockProject = createMockProject({ ownerId: mockOwner.id, platformId: mockPlatform.id }) + + await databaseConnection.getRepository('user').save([mockOwner]) + await databaseConnection.getRepository('platform').save([mockPlatform]) + await databaseConnection.getRepository('api_key').save([mockApiKey]) + await databaseConnection.getRepository('project').save([mockProject]) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockApiKey.value}`, + }, + query: { + projectId: mockProject.id, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + scope: EndpointScope.PROJECT, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).resolves.toBeUndefined() + + expect(mockRequest.principal).toEqual(expect.objectContaining({ + id: mockApiKey.id, + type: PrincipalType.SERVICE, + projectId: mockProject.id, + projectType: ProjectType.PLATFORM_MANAGED, + platform: { + id: mockPlatform.id, + role: PlatformRole.OWNER, + }, + })) + }) + + it('extracts projectId from resource if endpoint scope is PROJECT', async () => { + // arrange + const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() + const mockProject = createMockProject({ ownerId: mockOwner.id, platformId: mockPlatform.id }) + const mockFlow = createMockFlow({ projectId: mockProject.id }) + + await databaseConnection.getRepository('user').save([mockOwner]) + await databaseConnection.getRepository('platform').save([mockPlatform]) + await databaseConnection.getRepository('api_key').save([mockApiKey]) + await databaseConnection.getRepository('project').save([mockProject]) + await databaseConnection.getRepository('flow').save([mockFlow]) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows/:id', + params: { + id: mockFlow.id, + }, + headers: { + authorization: `Bearer ${mockApiKey.value}`, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + scope: EndpointScope.PROJECT, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).resolves.toBeUndefined() + + expect(mockRequest.principal).toEqual(expect.objectContaining({ + id: mockApiKey.id, + type: PrincipalType.SERVICE, + projectId: mockProject.id, + projectType: ProjectType.PLATFORM_MANAGED, + platform: { + id: mockPlatform.id, + role: PlatformRole.OWNER, + }, + })) + }) + + it('Fails if API key and project don\'t belong to same platform if endpoint scope is PROJECT', async () => { + // arrange + const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() + const { mockOwner: mockOtherOwner, mockPlatform: mockOtherPlatform } = createMockPlatformWithOwner() + const mockOtherProject = createMockProject({ ownerId: mockOtherOwner.id, platformId: mockOtherPlatform.id }) + + await databaseConnection.getRepository('user').save([mockOwner, mockOtherOwner]) + await databaseConnection.getRepository('platform').save([mockPlatform, mockOtherPlatform]) + await databaseConnection.getRepository('api_key').save([mockApiKey]) + await databaseConnection.getRepository('project').save([mockOtherProject]) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + query: { + projectId: mockOtherProject.id, + }, + headers: { + authorization: `Bearer ${mockApiKey.value}`, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + scope: EndpointScope.PROJECT, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid project id', + }, + })) + }) + + it('Fails if no projectId is extracted from request or resource and endpoint scope is PROJECT', async () => { + // arrange + const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() + + await databaseConnection.getRepository('user').save([mockOwner]) + await databaseConnection.getRepository('platform').save([mockPlatform]) + await databaseConnection.getRepository('api_key').save([mockApiKey]) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockApiKey.value}`, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + scope: EndpointScope.PROJECT, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid project id', + }, + })) + }) + + it('Fails if project with extracted id doesn\'t exist and endpoint scope is PROJECT', async () => { + // arrange + const mockNonExistentProjectId = apId() + const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() + + await databaseConnection.getRepository('user').save([mockOwner]) + await databaseConnection.getRepository('platform').save([mockPlatform]) + await databaseConnection.getRepository('api_key').save([mockApiKey]) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + query: { + projectId: mockNonExistentProjectId, + }, + headers: { + authorization: `Bearer ${mockApiKey.value}`, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + scope: EndpointScope.PROJECT, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid project id', + }, + })) + }) + + it('Fails if API key doesn\'t exist', async () => { + // arrange + const mockNonExistentApiKey = '123' + + const mockRequest = { + method: 'POST', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockNonExistentApiKey}`, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + scope: EndpointScope.PLATFORM, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.INVALID_BEARER_TOKEN, + params: { + message: 'invalid access token', + }, + })) + }) + + it('Fails if route doesn\'t allow SERVICE principals', async () => { + // arrange + const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() + + await databaseConnection.getRepository('user').save([mockOwner]) + await databaseConnection.getRepository('platform').save([mockPlatform]) + await databaseConnection.getRepository('api_key').save([mockApiKey]) + + const mockRequest = { + method: 'POST', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockApiKey.value}`, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.USER], + scope: EndpointScope.PLATFORM, + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid route for principal type', + }, + })) + }) + }) + + describe('Access Token Authentication', () => { + it('Authenticates users', async () => { + // arrange + const mockPrincipal: Principal = { + id: apId(), + type: PrincipalType.USER, + projectId: apId(), + projectType: ProjectType.PLATFORM_MANAGED, + platform: { + id: apId(), + role: PlatformRole.OWNER, + }, + } + + const mockAccessToken = await generateMockToken(mockPrincipal) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockAccessToken}`, + }, + routeConfig: {}, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).resolves.toBeUndefined() + + expect(mockRequest.principal).toEqual(expect.objectContaining({ + id: mockPrincipal.id, + type: PrincipalType.USER, + projectId: mockPrincipal.projectId, + projectType: ProjectType.PLATFORM_MANAGED, + platform: { + id: mockPrincipal.platform?.id, + role: PlatformRole.OWNER, + }, + })) + }) + + it('Fails if route disallows USER principal type', async () => { + // arrange + const mockAccessToken = await generateMockToken({ type: PrincipalType.USER }) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockAccessToken}`, + }, + routeConfig: { + allowedPrincipals: [PrincipalType.SERVICE], + }, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid route for principal type', + }, + })) + }) + + it('Fails if projectId in query doesn\'t match principal projectId', async () => { + // arrange + const mockProjectId = apId() + const mockOtherProjectId = apId() + const mockAccessToken = await generateMockToken({ projectId: mockProjectId }) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + query: { + projectId: mockOtherProjectId, + }, + headers: { + authorization: `Bearer ${mockAccessToken}`, + }, + routeConfig: {}, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid project id', + }, + })) + }) + + it('Fails if projectId in body doesn\'t match principal projectId', async () => { + // arrange + const mockProjectId = apId() + const mockOtherProjectId = apId() + const mockAccessToken = await generateMockToken({ projectId: mockProjectId }) + + const mockRequest = { + method: 'GET', + routerPath: '/v1/flows', + headers: { + authorization: `Bearer ${mockAccessToken}`, + }, + body: { + projectId: mockOtherProjectId, + }, + routeConfig: {}, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid project id', + }, + })) + }) + }) + + describe('Anonymous authentication', () => { + it('Enables access to non authenticated routes', async () => { + // arrange + const nonAuthenticatedRoute = '/v1/docs' + + const mockRequest = { + method: 'GET', + routerPath: nonAuthenticatedRoute, + headers: {}, + routeConfig: {}, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).resolves.toBeUndefined() + + expect(mockRequest.principal).toEqual(expect.objectContaining({ + id: expect.stringMatching(/ANONYMOUS_.{21}/), + type: PrincipalType.UNKNOWN, + projectId: expect.stringMatching(/ANONYMOUS_.{21}/), + projectType: ProjectType.STANDALONE, + })) + + expect(mockRequest.principal).not.toHaveProperty('platform') + }) + + it('Fails if route is authenticated', async () => { + // arrange + const authenticatedRoute = '/v1/flows' + + const mockRequest = { + method: 'GET', + routerPath: authenticatedRoute, + headers: {}, + routeConfig: {}, + } as unknown as FastifyRequest + + // act + const result = securityHandlerChain(mockRequest) + + // assert + await expect(result).rejects.toEqual(new ActivepiecesError({ + code: ErrorCode.AUTHORIZATION, + params: { + message: 'invalid route for principal type', + }, + })) + }) + }) +}) diff --git a/packages/backend/test/integration/cloud/flow-templates/flow-templates.test.ts b/packages/backend/test/integration/cloud/flow-templates/flow-templates.test.ts index ae4a56b1a..096978570 100644 --- a/packages/backend/test/integration/cloud/flow-templates/flow-templates.test.ts +++ b/packages/backend/test/integration/cloud/flow-templates/flow-templates.test.ts @@ -49,12 +49,12 @@ describe('Flow Templates', () => { // arrange const { mockPlatformTemplate } = await createMockPlatformTemplate({ platformId: CLOUD_PLATFORM_ID }) const _randomPlatformTemplate = await createMockPlatformTemplate({ platformId: apId() }) - + const response = await app?.inject({ method: 'GET', url: '/v1/flow-templates', }) - + // assert expect(response?.statusCode).toBe(StatusCodes.OK) const responseBody = response?.json() @@ -63,9 +63,9 @@ describe('Flow Templates', () => { }) }) - + describe('Delete Flow Template', () => { - + it('should not be able delete platform template as member', async () => { // arrange const { mockPlatform, mockPlatformTemplate } = await createMockPlatformTemplate({ platformId: apId() }) @@ -121,7 +121,7 @@ describe('Flow Templates', () => { }) // assert - expect(response?.statusCode).toBe(StatusCodes.UNAUTHORIZED) + expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) }) }) @@ -141,5 +141,5 @@ async function createMockPlatformTemplate({ platformId }: { platformId: string } const mockPlatformTemplate = createMockTemplate({ platformId: mockPlatform.id, projectId: mockProject.id, type: TemplateType.PLATFORM }) await databaseConnection.getRepository('flow_template').save(mockPlatformTemplate) - return { mockUser, mockPlatform, mockProject, mockPlatformTemplate } -} \ No newline at end of file + return { mockUser, mockPlatform, mockProject, mockPlatformTemplate } +} diff --git a/packages/backend/test/integration/cloud/git-repos/git-repos.test.ts b/packages/backend/test/integration/cloud/git-repos/git-repos.test.ts new file mode 100644 index 000000000..d668bdeef --- /dev/null +++ b/packages/backend/test/integration/cloud/git-repos/git-repos.test.ts @@ -0,0 +1,208 @@ +import { databaseConnection } from '../../../../src/app/database/database-connection' +import { setupApp } from '../../../../src/app/app' +import { FastifyInstance } from 'fastify' +import { StatusCodes } from 'http-status-codes' +import { faker } from '@faker-js/faker' +import { PrincipalType } from '@activepieces/shared' +import { createMockUser, createMockProject, createMockGitRepo } from '../../../helpers/mocks' +import { generateMockToken } from '../../../helpers/auth' + +let app: FastifyInstance | null = null + +beforeAll(async () => { + await databaseConnection.initialize() + app = await setupApp() +}) + +afterAll(async () => { + await databaseConnection.destroy() + await app?.close() +}) + +describe('Git API', () => { + describe('Create API', () => { + it('should not allow create git repo for other projects', async () => { + + const mockUser = createMockUser() + const mockUser2 = createMockUser() + await databaseConnection.getRepository('user').save([mockUser, mockUser2]) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + const mockProject2 = createMockProject({ ownerId: mockUser2.id }) + await databaseConnection.getRepository('project').save([mockProject, mockProject2]) + + + const request = { + projectId: mockProject2.id, + remoteUrl: `git@${faker.internet.url()}`, + sshPrivateKey: faker.hacker.noun(), + branch: 'main', + } + const token = await generateMockToken({ + id: mockUser.id, + projectId: mockProject.id, + type: PrincipalType.USER, + }) + + const response = await app?.inject({ + method: 'POST', + url: '/v1/git-repos', + payload: request, + headers: { + authorization: `Bearer ${token}`, + }, + }) + + expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) + }) + + it('should create a git repo', async () => { + + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save(mockUser) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save(mockProject) + + const request = { + projectId: mockProject.id, + remoteUrl: `git@${faker.internet.url()}`, + sshPrivateKey: faker.hacker.noun(), + branch: 'main', + } + const token = await generateMockToken({ + id: mockUser.id, + projectId: mockProject.id, + type: PrincipalType.USER, + }) + + const response = await app?.inject({ + method: 'POST', + url: '/v1/git-repos', + payload: request, + headers: { + authorization: `Bearer ${token}`, + }, + }) + + expect(response?.statusCode).toBe(StatusCodes.CREATED) + const responseBody = response?.json() + expect(Object.keys(responseBody).length).toBe(6) + expect(responseBody.sshPrivateKey).toBeUndefined() + expect(responseBody.remoteUrl).toBe(request.remoteUrl) + expect(responseBody.branch).toBe(request.branch) + expect(responseBody.created).toBeDefined() + expect(responseBody.updated).toBeDefined() + expect(responseBody.id).toBeDefined() + expect(responseBody.projectId).toBe(mockProject.id) + }) + }) + + describe('Delete API', () => { + + it('should delete a git repo', async () => { + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save(mockUser) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save(mockProject) + + const mockGitRepo = createMockGitRepo({ projectId: mockProject.id }) + await databaseConnection.getRepository('git_repo').save(mockGitRepo) + + const token = await generateMockToken({ + id: mockUser.id, + projectId: mockProject.id, + type: PrincipalType.USER, + }) + + const response = await app?.inject({ + method: 'DELETE', + url: '/v1/git-repos/' + mockGitRepo.id, + headers: { + authorization: `Bearer ${token}`, + }, + }) + expect(response?.statusCode).toBe(StatusCodes.NO_CONTENT) + }) + it('should not allow delete git repo for other projects', async () => { + + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save(mockUser) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + const mockProject2 = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save([mockProject, mockProject2]) + + const mockGitRepo = createMockGitRepo({ projectId: mockProject.id }) + const mockGitRepo2 = createMockGitRepo({ projectId: mockProject2.id }) + await databaseConnection.getRepository('git_repo').save([mockGitRepo, mockGitRepo2]) + + const token = await generateMockToken({ + id: mockUser.id, + projectId: mockProject.id, + type: PrincipalType.USER, + }) + + const response = await app?.inject({ + method: 'DELETE', + url: '/v1/git-repos/' + mockGitRepo2.id, + headers: { + authorization: `Bearer ${token}`, + }, + }) + + expect(response?.statusCode).toBe(StatusCodes.NOT_FOUND) + + }) + + }) + + describe('List API', () => { + + + it('should list a git repo', async () => { + + const mockUser = createMockUser() + await databaseConnection.getRepository('user').save(mockUser) + + const mockProject = createMockProject({ ownerId: mockUser.id }) + const mockProject2 = createMockProject({ ownerId: mockUser.id }) + await databaseConnection.getRepository('project').save([mockProject, mockProject2]) + + const mockGitRepo = createMockGitRepo({ projectId: mockProject.id }) + const mockGitRepo2 = createMockGitRepo({ projectId: mockProject2.id }) + await databaseConnection.getRepository('git_repo').save([mockGitRepo, mockGitRepo2]) + + const token = await generateMockToken({ + id: mockUser.id, + projectId: mockProject.id, + type: PrincipalType.USER, + }) + + const response = await app?.inject({ + method: 'GET', + url: '/v1/git-repos?projectId=' + mockProject.id, + headers: { + authorization: `Bearer ${token}`, + }, + }) + + expect(response?.statusCode).toBe(StatusCodes.OK) + const responseBody = response?.json() + expect(responseBody.data.length).toBe(1) + + const gitRepo = responseBody.data[0] + expect(Object.keys(gitRepo).length).toBe(6) + expect(gitRepo.sshPrivateKey).toBeUndefined() + expect(gitRepo.remoteUrl).toBe(mockGitRepo.remoteUrl) + expect(gitRepo.branch).toBe(mockGitRepo.branch) + expect(gitRepo.created).toBeDefined() + expect(gitRepo.updated).toBeDefined() + expect(gitRepo.id).toBeDefined() + expect(gitRepo.projectId).toBe(mockProject.id) + }) + }) + + +}) diff --git a/packages/backend/test/integration/cloud/managed-authn/external-token.test.ts b/packages/backend/test/integration/cloud/managed-authn/external-token.test.ts index 8938f331f..740ba2292 100644 --- a/packages/backend/test/integration/cloud/managed-authn/external-token.test.ts +++ b/packages/backend/test/integration/cloud/managed-authn/external-token.test.ts @@ -58,7 +58,7 @@ describe('Managed Authentication API', () => { const responseBody = response?.json() expect(response?.statusCode).toBe(StatusCodes.OK) - expect(Object.keys(responseBody)).toHaveLength(15) + expect(Object.keys(responseBody)).toHaveLength(16) expect(responseBody?.id).toHaveLength(21) expect(responseBody?.email).toBe(mockExternalTokenPayload.email) expect(responseBody?.firstName).toBe(mockExternalTokenPayload.firstName) @@ -66,7 +66,8 @@ describe('Managed Authentication API', () => { expect(responseBody?.trackEvents).toBe(true) expect(responseBody?.newsLetter).toBe(true) expect(responseBody?.password).toBeUndefined() - expect(responseBody?.status).toBe('VERIFIED') + expect(responseBody?.status).toBe('ACTIVE') + expect(responseBody?.verified).toBe(true) expect(responseBody?.externalId).toBe(mockExternalTokenPayload.externalUserId) expect(responseBody?.platformId).toBe(mockPlatform.id) expect(responseBody?.projectId).toHaveLength(21) diff --git a/packages/backend/test/integration/cloud/platform/admin-platform.test.ts b/packages/backend/test/integration/cloud/platform/admin-platform.test.ts index 872aee032..f5f889146 100644 --- a/packages/backend/test/integration/cloud/platform/admin-platform.test.ts +++ b/packages/backend/test/integration/cloud/platform/admin-platform.test.ts @@ -4,8 +4,8 @@ import { createMockProject, createMockUser } from '../../../helpers/mocks' import { StatusCodes } from 'http-status-codes' import { FastifyInstance } from 'fastify' import { faker } from '@faker-js/faker' -import { Project } from '@activepieces/shared' -import { LocalesEnum, Platform } from '@activepieces/ee-shared' +import { LocalesEnum, Project } from '@activepieces/shared' +import { Platform } from '@activepieces/ee-shared' let app: FastifyInstance | null = null @@ -47,8 +47,15 @@ describe('admin add platform endpoint', () => { // assert const responseBody = response?.json() expect(response?.statusCode).toBe(StatusCodes.CREATED) - expect(Object.keys(responseBody)).toHaveLength(23) + expect(Object.keys(responseBody)).toHaveLength(29) + expect(responseBody.allowedAuthDomains).toEqual([]) + expect(responseBody.emailAuthEnabled).toBe(true) + expect(responseBody.enforceAllowedAuthDomains).toBe(false) + expect(responseBody.ssoEnabled).toBe(false) expect(responseBody.id).toHaveLength(21) + expect(responseBody.federatedAuthProviders).toStrictEqual({}) + expect(responseBody.id).toHaveLength(21) + expect(responseBody.gitSyncEnabled).toBe(false) expect(responseBody.created).toBeDefined() expect(responseBody.updated).toBeDefined() expect(responseBody.ownerId).toBe(mockUser.id) @@ -68,7 +75,7 @@ describe('admin add platform endpoint', () => { expect(responseBody.privacyPolicyUrl).toBeNull() expect(responseBody.termsOfServiceUrl).toBeNull() expect(responseBody.cloudAuthEnabled).toBe(true) - expect(responseBody.embeddingEnabled).toBe(true) + expect(responseBody.embeddingEnabled).toBe(false) expect(responseBody.showPoweredBy).toBe(false) expect(responseBody.privacyPolicyUrl).toBeNull() expect(responseBody.termsOfServiceUrl).toBeNull() diff --git a/packages/backend/test/integration/cloud/platform/platform.test.ts b/packages/backend/test/integration/cloud/platform/platform.test.ts index ea22bfe90..625a516cc 100644 --- a/packages/backend/test/integration/cloud/platform/platform.test.ts +++ b/packages/backend/test/integration/cloud/platform/platform.test.ts @@ -4,8 +4,8 @@ import { generateMockToken } from '../../../helpers/auth' import { createMockUser, createMockPlatform } from '../../../helpers/mocks' import { StatusCodes } from 'http-status-codes' import { FastifyInstance } from 'fastify' -import { PlatformRole, PrincipalType, apId } from '@activepieces/shared' -import { FilteredPieceBehavior, LocalesEnum, UpdatePlatformRequestBody } from '@activepieces/ee-shared' +import { LocalesEnum, PlatformRole, PrincipalType, apId } from '@activepieces/shared' +import { FilteredPieceBehavior, UpdatePlatformRequestBody } from '@activepieces/ee-shared' let app: FastifyInstance | null = null @@ -39,6 +39,8 @@ describe('Platform API', () => { filteredPieceNames: ['updated filtered piece names'], filteredPieceBehavior: FilteredPieceBehavior.ALLOWED, smtpHost: 'updated smtp host', + enforceAllowedAuthDomains: true, + allowedAuthDomains: ['yahoo.com'], smtpPort: 123, smtpUser: 'updated smtp user', smtpPassword: 'updated smtp password', @@ -47,6 +49,7 @@ describe('Platform API', () => { privacyPolicyUrl: 'updated privacy policy url', termsOfServiceUrl: 'updated terms of service url', cloudAuthEnabled: false, + emailAuthEnabled: false, defaultLocale: LocalesEnum.ENGLISH, } // act @@ -63,11 +66,14 @@ describe('Platform API', () => { const responseBody = response?.json() expect(response?.statusCode).toBe(StatusCodes.OK) - expect(Object.keys(responseBody)).toHaveLength(23) + expect(Object.keys(responseBody)).toHaveLength(28) expect(responseBody.id).toBe(mockPlatform.id) expect(responseBody.created).toBeDefined() expect(responseBody.updated).toBeDefined() + expect(responseBody.enforceAllowedAuthDomains).toBe(requestBody.enforceAllowedAuthDomains) + expect(responseBody.allowedAuthDomains).toEqual(requestBody.allowedAuthDomains) expect(responseBody.ownerId).toBe(mockUser.id) + expect(responseBody.emailAuthEnabled).toBe(requestBody.emailAuthEnabled) expect(responseBody.name).toBe('updated name') expect(responseBody.primaryColor).toBe('updated primary color') expect(responseBody.logoIconUrl).toBe('updated logo icon url') @@ -75,10 +81,12 @@ describe('Platform API', () => { expect(responseBody.favIconUrl).toBe('updated fav icon url') expect(responseBody.filteredPieceNames).toStrictEqual(['updated filtered piece names']) expect(responseBody.filteredPieceBehavior).toBe('ALLOWED') + expect(responseBody.emailAuthEnabled).toBe(false) expect(responseBody.smtpHost).toBe('updated smtp host') expect(responseBody.smtpPort).toBe(123) expect(responseBody.smtpUser).toBe('updated smtp user') - expect(responseBody.smtpPassword).toBe('updated smtp password') + expect(responseBody.smtpPassword).toBeUndefined() + expect(responseBody.federatedAuthProviders).toStrictEqual({}) expect(responseBody.smtpSenderEmail).toBe('updated smtp sender email') expect(responseBody.smtpUseSSL).toBe(true) expect(responseBody.privacyPolicyUrl).toBe('updated privacy policy url') @@ -173,7 +181,7 @@ describe('Platform API', () => { expect(response?.statusCode).toBe(StatusCodes.OK) const responseBody = response?.json() - expect(Object.keys(responseBody)).toHaveLength(23) + expect(Object.keys(responseBody)).toHaveLength(28) expect(responseBody.id).toBe(mockPlatform.id) expect(responseBody.ownerId).toBe(mockOwnerUser.id) expect(responseBody.name).toBe(mockPlatform.name) diff --git a/packages/backend/test/integration/cloud/project-members/project-members.test.ts b/packages/backend/test/integration/cloud/project-members/project-members.test.ts index 9f5551d3b..e07f06ccc 100644 --- a/packages/backend/test/integration/cloud/project-members/project-members.test.ts +++ b/packages/backend/test/integration/cloud/project-members/project-members.test.ts @@ -51,7 +51,7 @@ describe('Project Member API', () => { }, body: mockInviteProjectMemberRequest, }) - expect(response?.statusCode).toBe(StatusCodes.UNAUTHORIZED) + expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) }) @@ -227,12 +227,11 @@ describe('Project Member API', () => { authorization: `Bearer ${mockApiKey.value}`, }, }) - expect(response?.statusCode).toBe(StatusCodes.UNAUTHORIZED) + expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) }) - }, - ) - + }) }) + describe('Delete project member Endpoint', () => { it('Deletes project member', async () => { const { mockUserToken, mockProject } = await createBasicEnvironment() @@ -295,7 +294,7 @@ describe('Project Member API', () => { authorization: `Bearer ${mockApiKey.value}`, }, }) - expect(response?.statusCode).toBe(StatusCodes.UNAUTHORIZED) + expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) }) }) }) @@ -338,4 +337,4 @@ async function createBasicEnvironment(embeddingEnabled = false): Promise<{ mockU mockUserToken, } -} \ No newline at end of file +} diff --git a/packages/backend/test/integration/cloud/user/enterprise-user.test.ts b/packages/backend/test/integration/cloud/user/enterprise-user.test.ts index 3b05425bb..8e8190a80 100644 --- a/packages/backend/test/integration/cloud/user/enterprise-user.test.ts +++ b/packages/backend/test/integration/cloud/user/enterprise-user.test.ts @@ -116,14 +116,14 @@ describe('Enterprise User API', () => { }) }) - describe('Suspend user endpoint', () => { - it('Updates user status to be SUSPENDED', async () => { + describe('Update user endpoint', () => { + it('Updates user status to be INACTIVE', async () => { // arrange const { mockOwner, mockPlatform } = createMockPlatformWithOwner() const mockUser = createMockUser({ platformId: mockPlatform.id, - status: UserStatus.VERIFIED, + status: UserStatus.ACTIVE, }) await databaseConnection.getRepository('user').save([mockOwner, mockUser]) @@ -141,19 +141,24 @@ describe('Enterprise User API', () => { // act const response = await app?.inject({ method: 'POST', - url: `/v1/users/${mockUser.id}/suspend`, + url: `/v1/users/${mockUser.id}`, headers: { authorization: `Bearer ${testToken}`, }, + body: { + status: UserStatus.INACTIVE, + }, }) // assert - expect(response?.statusCode).toBe(StatusCodes.NO_CONTENT) + expect(response?.statusCode).toBe(StatusCodes.OK) - const user = await databaseConnection.getRepository('user').findOneBy({ id: mockUser.id }) - expect(user?.status).toBe(UserStatus.SUSPENDED) + const responseJson = response?.json() + expect(responseJson.id).toBe(mockUser.id) + expect(responseJson.password).toBeUndefined() + expect(responseJson.status).toBe(UserStatus.INACTIVE) }) - + it('Fails if user doesn\'t exist', async () => { // arrange const nonExistentUserId = apId() @@ -169,23 +174,27 @@ describe('Enterprise User API', () => { // act const response = await app?.inject({ method: 'POST', - url: `/v1/users/${nonExistentUserId}/suspend`, + url: `/v1/users/${nonExistentUserId}`, headers: { authorization: `Bearer ${testToken}`, }, + body: { + status: UserStatus.INACTIVE, + }, }) // assert expect(response?.statusCode).toBe(StatusCodes.NOT_FOUND) + }) - it('Allows service accounts', async () => { + it('Allows service accounts to activate', async () => { // arrange const { mockOwner, mockPlatform, mockApiKey } = setupMockApiKeyServiceAccount() const mockUser = createMockUser({ platformId: mockPlatform.id, - status: UserStatus.VERIFIED, + status: UserStatus.INACTIVE, }) await databaseConnection.getRepository('user').save([mockOwner, mockUser]) @@ -195,17 +204,23 @@ describe('Enterprise User API', () => { // act const response = await app?.inject({ method: 'POST', - url: `/v1/users/${mockUser.id}/suspend`, + url: `/v1/users/${mockUser.id}`, headers: { authorization: `Bearer ${mockApiKey.value}`, }, + body: { + status: UserStatus.ACTIVE, + }, }) // assert - expect(response?.statusCode).toBe(StatusCodes.NO_CONTENT) + expect(response?.statusCode).toBe(StatusCodes.OK) - const user = await databaseConnection.getRepository('user').findOneBy({ id: mockUser.id }) - expect(user?.status).toBe(UserStatus.SUSPENDED) + + const responseJson = response?.json() + expect(responseJson.id).toBe(mockUser.id) + expect(responseJson.password).toBeUndefined() + expect(responseJson.status).toBe(UserStatus.ACTIVE) }) it('Requires principal to be platform owner', async () => { @@ -219,10 +234,13 @@ describe('Enterprise User API', () => { // act const response = await app?.inject({ method: 'POST', - url: `/v1/users/${mockUserId}/suspend`, + url: `/v1/users/${mockUserId}`, headers: { authorization: `Bearer ${testToken}`, }, + body: { + status: UserStatus.INACTIVE, + }, }) // assert diff --git a/packages/backend/test/integration/ee/authn/ee-authn.test.ts b/packages/backend/test/integration/ee/authn/ee-authn.test.ts index b22074309..3ee4576b6 100644 --- a/packages/backend/test/integration/ee/authn/ee-authn.test.ts +++ b/packages/backend/test/integration/ee/authn/ee-authn.test.ts @@ -43,7 +43,7 @@ describe('Authentication API', () => { expect(response?.statusCode).toBe(StatusCodes.OK) const responseBody = response?.json() - expect(Object.keys(responseBody)).toHaveLength(15) + expect(Object.keys(responseBody)).toHaveLength(16) expect(responseBody?.id).toHaveLength(21) expect(responseBody?.created).toBeDefined() expect(responseBody?.updated).toBeDefined() @@ -53,7 +53,8 @@ describe('Authentication API', () => { expect(responseBody?.trackEvents).toBe(mockSignUpRequest.trackEvents) expect(responseBody?.newsLetter).toBe(mockSignUpRequest.newsLetter) expect(responseBody?.password).toBeUndefined() - expect(responseBody?.status).toBe('VERIFIED') + expect(responseBody?.status).toBe('ACTIVE') + expect(responseBody?.verified).toBe(true) expect(responseBody?.platformId).toBeDefined() expect(responseBody?.externalId).toBe(null) expect(responseBody?.projectId).toHaveLength(21) @@ -93,6 +94,6 @@ describe('Authentication API', () => { expect(response?.statusCode).toBe(StatusCodes.FORBIDDEN) const responseBody = response?.json() - expect(responseBody?.code).toBe('INVITATIION_ONLY_SIGN_UP') + expect(responseBody?.code).toBe('INVITATION_ONLY_SIGN_UP') }) }) diff --git a/packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html b/packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html index fe1f25987..d76ddffe2 100644 --- a/packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html +++ b/packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html @@ -5,7 +5,7 @@ - Share template + Share Template
@@ -21,7 +21,7 @@ - Blog Url + Blog URL
Filters
diff --git a/packages/ee/components/src/lib/third-party-auth/third-party-auth.component.html b/packages/ee/components/src/lib/third-party-auth/third-party-auth.component.html index 60d39ecdb..4b67f2d87 100644 --- a/packages/ee/components/src/lib/third-party-auth/third-party-auth.component.html +++ b/packages/ee/components/src/lib/third-party-auth/third-party-auth.component.html @@ -23,14 +23,7 @@
-
-
-
- OR -
-
-
+ diff --git a/packages/ee/product-embed/product-sdk/src/app/disable-integration-modal/disable-integration-modal.component.html b/packages/ee/product-embed/product-sdk/src/app/disable-integration-modal/disable-integration-modal.component.html index 3687b8f1f..284823068 100644 --- a/packages/ee/product-embed/product-sdk/src/app/disable-integration-modal/disable-integration-modal.component.html +++ b/packages/ee/product-embed/product-sdk/src/app/disable-integration-modal/disable-integration-modal.component.html @@ -6,7 +6,7 @@ class="logo" [src]=" 'https://cdn.activepieces.com/pieces/' + - data.connection.appName.replace('@activepieces/piece-', '') + + data.connection.pieceName.replace('@activepieces/piece-', '') + '.png' " /> @@ -19,7 +19,7 @@

Are you sure you want to disconnect?

- You would need to re-connect to {{ data.connection.appName.replace('@activepieces/piece-', '') }} if you + You would need to re-connect to {{ data.connection.pieceName.replace('@activepieces/piece-', '') }} if you decide to enable it again.

diff --git a/packages/ee/product-embed/product-sdk/src/app/disable-integration-modal/disable-integration-modal.component.ts b/packages/ee/product-embed/product-sdk/src/app/disable-integration-modal/disable-integration-modal.component.ts index f0172d8cc..a6d94aac8 100644 --- a/packages/ee/product-embed/product-sdk/src/app/disable-integration-modal/disable-integration-modal.component.ts +++ b/packages/ee/product-embed/product-sdk/src/app/disable-integration-modal/disable-integration-modal.component.ts @@ -40,7 +40,7 @@ export class DisableIntegrationModalComponent { this.loading = true; this.delete$ = this.connectionService .delete({ - appName: this.connection.appName, + appName: this.connection.pieceName, projectId: getLocal(StorageName.PROJECT_ID), token: getLocal(StorageName.TOKEN), }) diff --git a/packages/ee/project-members/src/index.ts b/packages/ee/project-members/src/index.ts index 59341c125..bba90be0b 100644 --- a/packages/ee/project-members/src/index.ts +++ b/packages/ee/project-members/src/index.ts @@ -1,5 +1,5 @@ export * from './lib/service/project-members.service'; -export * from './lib/invite-project-member-dialog/invite-project-member.component'; +export * from './lib/dialogs/invite-project-member-dialog/invite-project-member.component'; export * from './lib/accept-invitation/accept-invitation.component'; export * from './lib/project-members-table/project-members-table.component'; export * from './lib/ee-project-members.module'; diff --git a/packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html b/packages/ee/project-members/src/lib/dialogs/invite-project-member-dialog/invite-project-member.component.html similarity index 84% rename from packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html rename to packages/ee/project-members/src/lib/dialogs/invite-project-member-dialog/invite-project-member.component.html index 4a7bedc7b..b5d423636 100755 --- a/packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html +++ b/packages/ee/project-members/src/lib/dialogs/invite-project-member-dialog/invite-project-member.component.html @@ -7,7 +7,7 @@
- + The invited email already has an account. Please contact support for assistance. @@ -20,7 +20,7 @@ Role - {{ projectMemberRoleText(role) }} + {{ RolesDisplayNames[role] }} @@ -33,11 +33,10 @@ Cancel - + Invite - + \ No newline at end of file diff --git a/packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts b/packages/ee/project-members/src/lib/dialogs/invite-project-member-dialog/invite-project-member.component.ts similarity index 87% rename from packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts rename to packages/ee/project-members/src/lib/dialogs/invite-project-member-dialog/invite-project-member.component.ts index f6c0341b1..ff3f3260a 100755 --- a/packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts +++ b/packages/ee/project-members/src/lib/dialogs/invite-project-member-dialog/invite-project-member.component.ts @@ -7,11 +7,12 @@ import { } from '@angular/forms'; import { MatSnackBar } from '@angular/material/snack-bar'; import { catchError, map, Observable, of, tap } from 'rxjs'; -import { ProjectMemberService } from '../service/project-members.service'; +import { ProjectMemberService } from '../../service/project-members.service'; import { DialogRef } from '@angular/cdk/dialog'; import { HttpStatusCode } from '@angular/common/http'; import { ProjectMemberRole } from '@activepieces/ee-shared'; import { AuthenticationService } from '@activepieces/ui/common'; +import { RolesDisplayNames } from '../../utils'; @Component({ templateUrl: './invite-project-member.component.html', @@ -25,6 +26,7 @@ export class InviteProjectMemberDialogComponent { inviteMember$: Observable | undefined; loading = false; invalidEmail = false; + RolesDisplayNames = RolesDisplayNames; constructor( private formBuilder: FormBuilder, private snackbar: MatSnackBar, @@ -89,17 +91,4 @@ export class InviteProjectMemberDialogComponent { get ProjectMemberRole() { return Object.keys(ProjectMemberRole); } - - projectMemberRoleText(role: string): string { - switch (role) { - case ProjectMemberRole.ADMIN: - return $localize`ADMIN`; - case ProjectMemberRole.EDITOR: - return $localize`EDITOR`; - case ProjectMemberRole.VIEWER: - return $localize`VIEWER`; - default: - return $localize`UNKNOWN`; - } - } } diff --git a/packages/ee/project-members/src/lib/ee-project-members.module.ts b/packages/ee/project-members/src/lib/ee-project-members.module.ts index 4ddfb1ccb..3907ed206 100644 --- a/packages/ee/project-members/src/lib/ee-project-members.module.ts +++ b/packages/ee/project-members/src/lib/ee-project-members.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AcceptInvitationComponent } from './accept-invitation/accept-invitation.component'; -import { InviteProjectMemberDialogComponent } from './invite-project-member-dialog/invite-project-member.component'; +import { InviteProjectMemberDialogComponent } from './dialogs/invite-project-member-dialog/invite-project-member.component'; import { UiCommonModule } from '@activepieces/ui/common'; import { ProjectMembersTableComponent } from './project-members-table/project-members-table.component'; import { EeBillingUiModule } from '@activepieces/ee-billing-ui'; diff --git a/packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html b/packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html index dc8d84e3d..c4f6fffbf 100644 --- a/packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html +++ b/packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html @@ -12,12 +12,12 @@ Status - {{ statusText(member.status) }} + {{ StatusDisplayNames[member.status] }} Role - {{ member.role }} + {{ RolesDisplayNames[member.role] }} diff --git a/packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts b/packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts index a92c33c8f..357d34b7e 100644 --- a/packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts +++ b/packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts @@ -13,7 +13,7 @@ import { take, } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; -import { InviteProjectMemberDialogComponent } from '../invite-project-member-dialog/invite-project-member.component'; +import { InviteProjectMemberDialogComponent } from '../dialogs/invite-project-member-dialog/invite-project-member.component'; import { ProjectMemberRole, ProjectMemberStatus, @@ -25,6 +25,7 @@ import { AuthenticationService, ProjectSelectors, } from '@activepieces/ui/common'; +import { RolesDisplayNames } from '../utils'; @Component({ selector: 'app-project-members-table', @@ -42,6 +43,11 @@ export class ProjectMembersTableComponent implements OnInit { refreshTableAtCurrentCursor$: Subject = new Subject(); displayedColumns = ['email', 'role', 'status', 'created', 'action']; title = $localize`Project Members`; + RolesDisplayNames = RolesDisplayNames; + StatusDisplayNames: { [k: string]: string } = { + [ProjectMemberStatus.ACTIVE]: $localize`Active`, + [ProjectMemberStatus.PENDING]: $localize`Pending`, + }; constructor( private matDialog: MatDialog, private billingService: BillingService, @@ -135,15 +141,6 @@ export class ProjectMembersTableComponent implements OnInit { ); } - statusText(status: ProjectMemberStatus) { - switch (status) { - case ProjectMemberStatus.ACTIVE: - return $localize`Active`; - case ProjectMemberStatus.PENDING: - return $localize`Pending`; - } - } - get projectMemberRole() { return ProjectMemberRole; } diff --git a/packages/ee/project-members/src/lib/utils.ts b/packages/ee/project-members/src/lib/utils.ts new file mode 100644 index 000000000..08a1aea07 --- /dev/null +++ b/packages/ee/project-members/src/lib/utils.ts @@ -0,0 +1,7 @@ +import { ProjectMemberRole } from '@activepieces/ee-shared'; + +export const RolesDisplayNames: { [k: string]: string } = { + [ProjectMemberRole.ADMIN]: $localize`Admin`, + [ProjectMemberRole.EDITOR]: $localize`Editor`, + [ProjectMemberRole.VIEWER]: $localize`Viewer`, +}; diff --git a/packages/ee/shared/src/index.ts b/packages/ee/shared/src/index.ts index 180187c41..1e968cb2f 100644 --- a/packages/ee/shared/src/index.ts +++ b/packages/ee/shared/src/index.ts @@ -1,3 +1,4 @@ +export * from './lib/git-repo'; export * from './lib/api-key'; export * from './lib/project/project-with-usage-and-plan'; export * from './lib/billing'; diff --git a/packages/ee/shared/src/lib/authn/enterprise-local-authn/requests.ts b/packages/ee/shared/src/lib/authn/enterprise-local-authn/requests.ts index 0be3a5312..6c0cc8eab 100644 --- a/packages/ee/shared/src/lib/authn/enterprise-local-authn/requests.ts +++ b/packages/ee/shared/src/lib/authn/enterprise-local-authn/requests.ts @@ -1,4 +1,4 @@ -import { ApId, SignUpRequest } from '@activepieces/shared' +import { ApId, SignUpRequest, UserStatus } from '@activepieces/shared' import { Static, Type } from '@sinclair/typebox' export const VerifyEmailRequestBody = Type.Object({ @@ -22,3 +22,8 @@ export const SignUpAndAcceptRequestBody = Type.Composite([ ]) export type SignUpAndAcceptRequestBody = Static + +export const UpdateUserRequestBody = Type.Object({ + status: Type.Enum(UserStatus) +}) + diff --git a/packages/ee/shared/src/lib/authn/federated-authn/authn-provider-name.ts b/packages/ee/shared/src/lib/authn/federated-authn/authn-provider-name.ts index 743a0c21b..c93518efa 100644 --- a/packages/ee/shared/src/lib/authn/federated-authn/authn-provider-name.ts +++ b/packages/ee/shared/src/lib/authn/federated-authn/authn-provider-name.ts @@ -1,6 +1,6 @@ export enum ThirdPartyAuthnProviderEnum { - GOOGLE = 'GOOGLE', - GITHUB = 'GITHUB', + GOOGLE = 'google', + GITHUB = 'github', } export type ThirdPartyAuthnProvidersToShowMap = { diff --git a/packages/ee/shared/src/lib/authn/federated-authn/index.ts b/packages/ee/shared/src/lib/authn/federated-authn/index.ts index a05e4c27d..c7c12b29b 100644 --- a/packages/ee/shared/src/lib/authn/federated-authn/index.ts +++ b/packages/ee/shared/src/lib/authn/federated-authn/index.ts @@ -4,7 +4,7 @@ import { ThirdPartyAuthnProviderEnum } from './authn-provider-name'; export * from './authn-provider-name' export const federatedAuthnLoginResponse = Type.Object({ - loginUrl :Type.String() + loginUrl: Type.String() }) export type FederatedAuthnLoginResponse = Static; @@ -14,4 +14,28 @@ export const ClaimTokenRequest = Type.Object({ code: Type.String(), }) -export type ClaimTokenRequest = Static \ No newline at end of file +export type ClaimTokenRequest = Static + +export const GoogleAuthnProviderConfig = Type.Object({ + clientId: Type.String(), + clientSecret: Type.String(), +}) +export type GoogleAuthnProviderConfig = Static + +export const GithubAuthnProviderConfig = Type.Object({ + clientId: Type.String(), + clientSecret: Type.String(), +}) +export type GithubAuthnProviderConfig = Static + + +export const FederatedAuthnProviderConfig = Type.Object({ + google: Type.Optional(GoogleAuthnProviderConfig), + github: Type.Optional(GithubAuthnProviderConfig), +}) +export type FederatedAuthnProviderConfig = Static + +export const FederatedAuthnProviderConfigWithoutSensitiveData = Type.Object({ + google: Type.Optional(Type.Omit(GoogleAuthnProviderConfig, ['clientSecret'])), + github: Type.Optional(Type.Omit(GithubAuthnProviderConfig, ['clientSecret'])), +}) \ No newline at end of file diff --git a/packages/ee/shared/src/lib/git-repo/index.ts b/packages/ee/shared/src/lib/git-repo/index.ts new file mode 100644 index 000000000..dd17d6b04 --- /dev/null +++ b/packages/ee/shared/src/lib/git-repo/index.ts @@ -0,0 +1,32 @@ +import { Static, Type } from "@sinclair/typebox"; +import { BaseModelSchema } from "@activepieces/shared"; + +export const GitRepo = Type.Object({ + ...BaseModelSchema, + remoteUrl: Type.String(), + branch: Type.String(), + projectId: Type.String(), + sshPrivateKey: Type.String(), +}) + +export type GitRepo = Static + +export const GitRepoWithoutSenestiveData = Type.Omit(GitRepo, ['sshPrivateKey']) +export type GitRepoWithoutSenestiveData = Static + +export const PushGitRepoRequest = Type.Object({ + commitMessage: Type.String(), +}) + +export type PushGitRepoRequest = Static + +export const ConfigureRepoRequest = Type.Object({ + projectId: Type.String(), + remoteUrl: Type.String({ + pattern: '^git@', + }), + branch: Type.String(), + sshPrivateKey: Type.String(), +}) + +export type ConfigureRepoRequest = Static diff --git a/packages/ee/shared/src/lib/platform/platform.model.ts b/packages/ee/shared/src/lib/platform/platform.model.ts index b6802340a..7435149ea 100644 --- a/packages/ee/shared/src/lib/platform/platform.model.ts +++ b/packages/ee/shared/src/lib/platform/platform.model.ts @@ -1,5 +1,6 @@ -import { ApId, BaseModelSchema } from "@activepieces/shared"; +import { ApId, BaseModelSchema, LocalesEnum } from "@activepieces/shared"; import { Static, Type } from "@sinclair/typebox"; +import { FederatedAuthnProviderConfig, FederatedAuthnProviderConfigWithoutSensitiveData } from "../authn"; export type PlatformId = ApId; @@ -7,19 +8,6 @@ export enum FilteredPieceBehavior { ALLOWED = 'ALLOWED', BLOCKED = 'BLOCKED', } -export enum LocalesEnum { - DUTCH = 'nl', - ENGLISH = 'en', - GERMAN = 'de', - ITALIAN = 'it', - FRENCH = 'fr', - SPANISH = 'es', - JAPANESE = 'ja', - INDONESIAN = 'id', - VIETNAMESE = 'vi', - CHINESE_SIMPLIFIED = 'zh', - PORTUGUESE = 'pt' -} export const Platform = Type.Object({ ...BaseModelSchema, @@ -40,9 +28,21 @@ export const Platform = Type.Object({ privacyPolicyUrl: Type.Optional(Type.String()), termsOfServiceUrl: Type.Optional(Type.String()), cloudAuthEnabled: Type.Boolean(), + gitSyncEnabled: Type.Boolean(), showPoweredBy: Type.Boolean(), embeddingEnabled: Type.Boolean(), - defaultLocale: Type.Optional(Type.Enum(LocalesEnum)) + defaultLocale: Type.Optional(Type.Enum(LocalesEnum)), + ssoEnabled: Type.Boolean(), + enforceAllowedAuthDomains: Type.Boolean(), + allowedAuthDomains: Type.Array(Type.String()), + federatedAuthProviders: FederatedAuthnProviderConfig, + emailAuthEnabled: Type.Boolean(), }) export type Platform = Static + +export const PlatformWithoutSensitiveData = Type.Composite([Type.Object({ + federatedAuthProviders: FederatedAuthnProviderConfigWithoutSensitiveData, +}), Type.Omit(Platform, ['smtpPassword', 'federatedAuthProviders'])] ) + +export type PlatformWithoutSensitiveData = Static \ No newline at end of file diff --git a/packages/ee/shared/src/lib/platform/platform.request.ts b/packages/ee/shared/src/lib/platform/platform.request.ts index 9ff1782c7..dc93e062c 100644 --- a/packages/ee/shared/src/lib/platform/platform.request.ts +++ b/packages/ee/shared/src/lib/platform/platform.request.ts @@ -1,6 +1,7 @@ -import { ApId } from '@activepieces/shared' +import { ApId, LocalesEnum } from '@activepieces/shared' import { Type, Static } from '@sinclair/typebox' -import { FilteredPieceBehavior, LocalesEnum } from './platform.model' +import { FilteredPieceBehavior } from './platform.model' +import { FederatedAuthnProviderConfig } from '../authn' export const UpdatePlatformRequestBody = Type.Object({ name: Type.Optional(Type.String()), @@ -17,8 +18,12 @@ export const UpdatePlatformRequestBody = Type.Object({ smtpSenderEmail: Type.Optional(Type.String()), smtpUseSSL: Type.Optional(Type.Boolean()), privacyPolicyUrl: Type.Optional(Type.String()), + federatedAuthProviders: Type.Optional(FederatedAuthnProviderConfig), termsOfServiceUrl: Type.Optional(Type.String()), cloudAuthEnabled: Type.Optional(Type.Boolean()), + emailAuthEnabled: Type.Optional(Type.Boolean()), + allowedAuthDomains: Type.Optional(Type.Array(Type.String())), + enforceAllowedAuthDomains: Type.Optional(Type.Boolean()), defaultLocale: Type.Optional(Type.Enum(LocalesEnum)) }) diff --git a/packages/ee/shared/src/lib/project/project-requests.ts b/packages/ee/shared/src/lib/project/project-requests.ts index 47085cf3b..02c57de3e 100644 --- a/packages/ee/shared/src/lib/project/project-requests.ts +++ b/packages/ee/shared/src/lib/project/project-requests.ts @@ -7,13 +7,14 @@ export const UpdateProjectPlatformRequest = Type.Object({ plan: Type.Optional(Type.Object({ teamMembers: Type.Number({}), tasks: Type.Number({}), - })) + })), }) export type UpdateProjectPlatformRequest = Static; export const CreatePlatformProjectRequest = Type.Object({ displayName: Type.String(), + externalId: Type.Optional(Type.String()), }) export type CreatePlatformProjectRequest = Static; diff --git a/packages/ee/ui-platform/src/lib/components/allowed-email-domains-list/allowed-email-domains-list.component.html b/packages/ee/ui-platform/src/lib/components/allowed-email-domains-list/allowed-email-domains-list.component.html new file mode 100644 index 000000000..56c258e30 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/allowed-email-domains-list/allowed-email-domains-list.component.html @@ -0,0 +1,36 @@ +
+
+
+ + +
+
Allowed Email Domains
+
+ Anyone with an email address at these domains is allowed to sign up for your platform. +
Keep empty to allow all domains. +
+
+ + +
+ + Add + Domain +
+
+ {{ + domain + }} + +
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/packages/ee/ui-platform/src/lib/components/allowed-email-domains-list/allowed-email-domains-list.component.ts b/packages/ee/ui-platform/src/lib/components/allowed-email-domains-list/allowed-email-domains-list.component.ts new file mode 100644 index 000000000..5aa616864 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/allowed-email-domains-list/allowed-email-domains-list.component.ts @@ -0,0 +1,95 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; +import { Platform } from '@activepieces/ee-shared'; +import { MatDialog } from '@angular/material/dialog'; +import { Observable, catchError, of, tap } from 'rxjs'; +import { AddAllowedEmailDomainDialogComponent } from '../dialogs/add-allowed-email-domain-dialog/add-allowed-email-domain-dialog.component'; +import { + GenericSnackbarTemplateComponent, + PlatformService, + unexpectedErrorMessage, +} from '@activepieces/ui/common'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Component({ + selector: 'app-allowed-email-domains-list', + templateUrl: './allowed-email-domains-list.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AllowedEmailDomainsListComponent { + @Input({ required: true }) platform!: Platform; + @Output() platformUpdated = new EventEmitter(); + domainsBeingRemoved: Record = {}; + addDomain$?: Observable; + removeDomain$?: Observable; + constructor( + private matDialog: MatDialog, + private platformService: PlatformService, + private matSnackbar: MatSnackBar + ) {} + addDomain() { + this.addDomain$ = this.matDialog + .open(AddAllowedEmailDomainDialogComponent, { + data: { + platform: this.platform, + }, + }) + .afterClosed() + .pipe( + tap((domain) => { + if (domain) { + const platform: Platform = JSON.parse( + JSON.stringify(this.platform) + ); + platform.allowedAuthDomains.push(domain); + this.platformUpdated.emit(platform); + this.matSnackbar.openFromComponent( + GenericSnackbarTemplateComponent, + { + data: `Added ${domain}`, + } + ); + } + }) + ); + } + removeDomain(domain: string) { + const platform: Platform = JSON.parse(JSON.stringify(this.platform)); + platform.allowedAuthDomains = platform.allowedAuthDomains.filter( + (d) => d !== domain + ); + platform.enforceAllowedAuthDomains = platform.allowedAuthDomains.length > 0; + this.domainsBeingRemoved[domain] = true; + this.removeDomain$ = this.platformService + .updatePlatform( + { + enforceAllowedAuthDomains: platform.enforceAllowedAuthDomains, + allowedAuthDomains: platform.allowedAuthDomains, + }, + platform.id + ) + .pipe( + tap(() => { + this.platformUpdated.emit(platform); + this.matSnackbar.openFromComponent(GenericSnackbarTemplateComponent, { + data: `Removed ${domain}`, + }); + this.domainsBeingRemoved[domain] = false; + }), + catchError((err) => { + console.error(err); + this.matSnackbar.open(unexpectedErrorMessage, '', { + panelClass: 'error', + }); + platform.allowedAuthDomains.push(domain); + this.platformUpdated.emit(platform); + return of(void 0); + }) + ); + } +} diff --git a/packages/ee/ui-platform/src/lib/components/configure-allowing-email-logins-card/configure-allowing-email-logins-card.component.html b/packages/ee/ui-platform/src/lib/components/configure-allowing-email-logins-card/configure-allowing-email-logins-card.component.html new file mode 100644 index 000000000..26702ff22 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/configure-allowing-email-logins-card/configure-allowing-email-logins-card.component.html @@ -0,0 +1,25 @@ +
+
+ + +
+
Allow Email Logins
+
+ Allow logins through email and password. +
+ +
+
+
+
+
+ + +
+ + + +
+ \ No newline at end of file diff --git a/packages/ee/ui-platform/src/lib/components/configure-allowing-email-logins-card/configure-allowing-email-logins-card.component.ts b/packages/ee/ui-platform/src/lib/components/configure-allowing-email-logins-card/configure-allowing-email-logins-card.component.ts new file mode 100644 index 000000000..8cb9e542c --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/configure-allowing-email-logins-card/configure-allowing-email-logins-card.component.ts @@ -0,0 +1,65 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnInit, + Output, +} from '@angular/core'; +import { Platform } from '@activepieces/ee-shared'; +import { Observable, tap } from 'rxjs'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { PlatformService } from '@activepieces/ui/common'; +import { + AtLeastOneLoginMethodMsg, + doesPlatformHaveAtLeastOneLoginMethodEnabled, +} from '../util'; +import { MatSlideToggleChange } from '@angular/material/slide-toggle'; + +@Component({ + selector: 'app-configure-allowing-email-logins-card', + templateUrl: './configure-allowing-email-logins-card.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ConfigureAllowingEmailLoginsCardComponent implements OnInit { + @Input({ required: true }) platform!: Platform; + @Output() platformUpdated = new EventEmitter(); + + toggleEmailAuthnEnabled$?: Observable; + toggleDisabled = false; + toggleChecked = false; + constructor( + private matSnackbar: MatSnackBar, + private platformService: PlatformService + ) {} + ngOnInit() { + this.toggleChecked = this.platform.emailAuthEnabled; + } + + toggleClicked($event: MatSlideToggleChange) { + const platform: Platform = JSON.parse(JSON.stringify(this.platform)); + platform.emailAuthEnabled = !platform.emailAuthEnabled; + if (!doesPlatformHaveAtLeastOneLoginMethodEnabled(platform)) { + this.matSnackbar.open(AtLeastOneLoginMethodMsg); + $event.source.checked = true; + return; + } + this.toggleDisabled = true; + this.platformUpdated.emit(platform); + this.toggleEmailAuthnEnabled$ = this.platformService + .updatePlatform( + { emailAuthEnabled: platform.emailAuthEnabled }, + platform.id + ) + .pipe( + tap(() => { + this.toggleDisabled = false; + if (platform.emailAuthEnabled) { + this.matSnackbar.open($localize`Email logins enabled`); + } else { + this.matSnackbar.open($localize`Email logins disabled`); + } + }) + ); + } +} diff --git a/packages/ee/ui-platform/src/lib/components/configure-confederated-authn-card/configure-confederated-authn-card.component.html b/packages/ee/ui-platform/src/lib/components/configure-confederated-authn-card/configure-confederated-authn-card.component.html new file mode 100644 index 000000000..231c6bea8 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/configure-confederated-authn-card/configure-confederated-authn-card.component.html @@ -0,0 +1,27 @@ +
+
+ + +
+
{{federatedAuthnProvider}}
+
+ Allow logins through {{federatedAuthnProvider}}'s single sign-on functionlaity. +
+ +
+
+
+
+
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/packages/ee/ui-platform/src/lib/components/configure-confederated-authn-card/configure-confederated-authn-card.component.ts b/packages/ee/ui-platform/src/lib/components/configure-confederated-authn-card/configure-confederated-authn-card.component.ts new file mode 100644 index 000000000..690e34c76 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/configure-confederated-authn-card/configure-confederated-authn-card.component.ts @@ -0,0 +1,119 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnInit, + Output, +} from '@angular/core'; +import { Platform } from '@activepieces/ee-shared'; +import { Observable, tap } from 'rxjs'; +import { + EnableFederatedAuthnProviderDialogComponent, + EnableFederatedAuthnProviderDialogData, +} from '../dialogs/enable-federated-authn-provider-dialog/enable-federated-authn-provider-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { + GenericSnackbarTemplateComponent, + PlatformService, +} from '@activepieces/ui/common'; +import { FederatedAuthnProviderEnum } from '../sso-settings/federated-authn-provider.enum'; +import { MatSlideToggleChange } from '@angular/material/slide-toggle'; +import { + AtLeastOneLoginMethodMsg, + doesPlatformHaveAtLeastOneLoginMethodEnabled, +} from '../util'; + +@Component({ + selector: 'app-configure-confederated-authn-card', + templateUrl: './configure-confederated-authn-card.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ConfigureConfederatedAuthnCardComponent implements OnInit { + @Input({ required: true }) platform!: Platform; + @Input({ required: true }) + federatedAuthnProvider!: FederatedAuthnProviderEnum; + @Output() platformUpdated = new EventEmitter(); + enableFederatedAuthn$?: Observable; + disableFederatedAuthn$?: Observable; + toggleDisabled = false; + toggleChecked = false; + constructor( + private matDialog: MatDialog, + private matSnackbar: MatSnackBar, + private platformService: PlatformService + ) {} + ngOnInit() { + this.toggleChecked = + this.federatedAuthnProvider === 'Google' + ? this.platform.federatedAuthProviders.google !== undefined + : this.platform.federatedAuthProviders.github !== undefined; + } + disableAuthnProvider($event: MatSlideToggleChange) { + const platform: Platform = JSON.parse(JSON.stringify(this.platform)); + + if (this.federatedAuthnProvider === 'Google') { + delete platform.federatedAuthProviders.google; + } else if (this.federatedAuthnProvider === 'Github') { + delete platform.federatedAuthProviders.github; + } + if (!doesPlatformHaveAtLeastOneLoginMethodEnabled(platform)) { + this.matSnackbar.open(AtLeastOneLoginMethodMsg); + $event.source.checked = true; + return; + } + + this.toggleDisabled = true; + this.disableFederatedAuthn$ = this.platformService + .updatePlatform( + { federatedAuthProviders: platform.federatedAuthProviders }, + platform.id + ) + .pipe( + tap(() => { + this.toggleDisabled = false; + this.matSnackbar.openFromComponent(GenericSnackbarTemplateComponent, { + data: `${this.federatedAuthnProvider} SSO disabled`, + }); + }) + ); + this.platformUpdated.emit(platform); + this.toggleChecked = false; + } + enableAuthnProvider() { + const data: EnableFederatedAuthnProviderDialogData = { + platform: this.platform, + provider: this.federatedAuthnProvider, + }; + this.enableFederatedAuthn$ = this.matDialog + .open(EnableFederatedAuthnProviderDialogComponent, { data }) + .afterClosed() + .pipe( + tap((platform) => { + if (platform) { + this.toggleChecked = true; + this.platformUpdated.emit(platform); + this.matSnackbar.openFromComponent( + GenericSnackbarTemplateComponent, + { + data: `${this.federatedAuthnProvider} SSO enabled`, + } + ); + } + }) + ); + } + toggleClicked($event: MatSlideToggleChange) { + $event.source.checked = this.toggleChecked; + const federatedAuthnProviderValue = + this.federatedAuthnProvider === FederatedAuthnProviderEnum.Github + ? this.platform.federatedAuthProviders.github + : this.platform.federatedAuthProviders.google; + if (federatedAuthnProviderValue === undefined) { + this.enableAuthnProvider(); + } else { + this.disableAuthnProvider($event); + } + } +} diff --git a/packages/ee/ui-platform/src/lib/components/dialogs/add-allowed-email-domain-dialog/add-allowed-email-domain-dialog.component.html b/packages/ee/ui-platform/src/lib/components/dialogs/add-allowed-email-domain-dialog/add-allowed-email-domain-dialog.component.html new file mode 100644 index 000000000..1fd6b09f8 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/dialogs/add-allowed-email-domain-dialog/add-allowed-email-domain-dialog.component.html @@ -0,0 +1,34 @@ + + {{title}} + + + +
+ +
+ + Domain + + + Domain is required + + +
+
+ +
+ + +
+ + Cancel + + + Confirm + +
+
+ \ No newline at end of file diff --git a/packages/ee/ui-platform/src/lib/components/dialogs/add-allowed-email-domain-dialog/add-allowed-email-domain-dialog.component.ts b/packages/ee/ui-platform/src/lib/components/dialogs/add-allowed-email-domain-dialog/add-allowed-email-domain-dialog.component.ts new file mode 100644 index 000000000..55fbc341f --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/dialogs/add-allowed-email-domain-dialog/add-allowed-email-domain-dialog.component.ts @@ -0,0 +1,58 @@ +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { + FormBuilder, + FormControl, + FormGroup, + Validators, +} from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { BehaviorSubject, Observable, tap } from 'rxjs'; +import { Platform } from '@activepieces/ee-shared'; +import { ApEdition } from '@activepieces/shared'; +import { PlatformService } from '@activepieces/ui/common'; + +@Component({ + selector: 'app-add-allowed-email-domain-dialog', + templateUrl: './add-allowed-email-domain-dialog.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AddAllowedEmailDomainDialogComponent { + readonly ApEdition = ApEdition; + readonly title = $localize`Add Allowed Email Domain`; + loading$ = new BehaviorSubject(false); + addAllowedDomain$?: Observable; + formGroup: FormGroup<{ + domain: FormControl; + }>; + constructor( + private fb: FormBuilder, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private data: { platform: Platform }, + private platformService: PlatformService + ) { + this.formGroup = this.fb.group({ + domain: this.fb.control('', { + nonNullable: true, + validators: [Validators.required], + }), + }); + } + + submit() { + this.formGroup.markAllAsTouched(); + if (!this.loading$.value && this.formGroup.valid) { + this.loading$.next(true); + const platform: Platform = JSON.parse(JSON.stringify(this.data.platform)); + platform.allowedAuthDomains.push(this.formGroup.getRawValue().domain); + this.addAllowedDomain$ = this.platformService + .updatePlatform( + { + enforceAllowedAuthDomains: true, + allowedAuthDomains: platform.allowedAuthDomains, + }, + platform.id + ) + .pipe(tap(() => this.dialogRef.close(this.formGroup.value.domain))); + } + } +} diff --git a/packages/ee/ui-platform/src/lib/components/dialogs/enable-federated-authn-provider-dialog/enable-federated-authn-provider-dialog.component.html b/packages/ee/ui-platform/src/lib/components/dialogs/enable-federated-authn-provider-dialog/enable-federated-authn-provider-dialog.component.html new file mode 100644 index 000000000..d21484123 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/dialogs/enable-federated-authn-provider-dialog/enable-federated-authn-provider-dialog.component.html @@ -0,0 +1,42 @@ + + {{title}} + + + +
+ +
+ + Client ID + + + Client ID is required + + + + Client Secret + + + Client secret is required + + +
+
+ +
+ + +
+ + Cancel + + + Confirm + +
+
+ \ No newline at end of file diff --git a/packages/ee/ui-platform/src/lib/components/dialogs/enable-federated-authn-provider-dialog/enable-federated-authn-provider-dialog.component.ts b/packages/ee/ui-platform/src/lib/components/dialogs/enable-federated-authn-provider-dialog/enable-federated-authn-provider-dialog.component.ts new file mode 100644 index 000000000..2ff9adbaf --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/dialogs/enable-federated-authn-provider-dialog/enable-federated-authn-provider-dialog.component.ts @@ -0,0 +1,77 @@ +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { + FormBuilder, + FormControl, + FormGroup, + Validators, +} from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { BehaviorSubject, Observable, tap } from 'rxjs'; +import { Platform } from '@activepieces/ee-shared'; +import { ApEdition } from '@activepieces/shared'; +import { PlatformService } from '@activepieces/ui/common'; +import { FederatedAuthnProviderEnum } from '../../sso-settings/federated-authn-provider.enum'; +export type EnableFederatedAuthnProviderDialogData = { + platform: Platform; + provider: FederatedAuthnProviderEnum; +}; + +@Component({ + selector: 'app-enable-federated-authn-provider-dialog', + templateUrl: './enable-federated-authn-provider-dialog.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class EnableFederatedAuthnProviderDialogComponent { + readonly ApEdition = ApEdition; + readonly title: string = ''; + loading$ = new BehaviorSubject(false); + enableProvider$?: Observable; + formGroup: FormGroup<{ + clientId: FormControl; + clientSecret: FormControl; + }>; + constructor( + private fb: FormBuilder, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) + private data: EnableFederatedAuthnProviderDialogData, + private platformService: PlatformService + ) { + this.formGroup = this.fb.group({ + clientId: this.fb.control('', { + nonNullable: true, + validators: [Validators.required], + }), + clientSecret: this.fb.control('', { + nonNullable: true, + validators: [Validators.required], + }), + }); + this.title = $localize`${this.data.provider} SSO`; + } + + submit() { + this.formGroup.markAllAsTouched(); + if (!this.loading$.value && this.formGroup.valid) { + this.loading$.next(true); + const platform: Platform = JSON.parse(JSON.stringify(this.data.platform)); + const formData = this.formGroup.getRawValue(); + if (this.data.provider === 'Github') { + platform.federatedAuthProviders.github = formData; + } + if (this.data.provider === 'Google') { + platform.federatedAuthProviders.google = formData; + } + this.enableProvider$ = this.platformService + .updatePlatform( + { federatedAuthProviders: platform.federatedAuthProviders }, + platform.id + ) + .pipe( + tap(() => { + this.dialogRef.close(platform); + }) + ); + } + } +} diff --git a/packages/ee/ui-platform/src/lib/components/smtp-settings/smtp-settings.component.ts b/packages/ee/ui-platform/src/lib/components/smtp-settings/smtp-settings.component.ts index 03b4db91f..834e157a6 100644 --- a/packages/ee/ui-platform/src/lib/components/smtp-settings/smtp-settings.component.ts +++ b/packages/ee/ui-platform/src/lib/components/smtp-settings/smtp-settings.component.ts @@ -80,7 +80,7 @@ export class SmtpSettingsComponent implements OnInit { .pipe( tap(() => { this.loading$.next(false); - this.matSnackbar.open('Saved successfully'); + this.matSnackbar.open($localize`Saved successfully`); }), catchError((err) => { this.loading$.next(false); diff --git a/packages/ee/ui-platform/src/lib/components/sso-settings/federated-authn-provider.enum.ts b/packages/ee/ui-platform/src/lib/components/sso-settings/federated-authn-provider.enum.ts new file mode 100644 index 000000000..d7cd6512b --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/sso-settings/federated-authn-provider.enum.ts @@ -0,0 +1,4 @@ +export enum FederatedAuthnProviderEnum { + Google = 'Google', + Github = 'Github', +} diff --git a/packages/ee/ui-platform/src/lib/components/sso-settings/sso-settings.component.html b/packages/ee/ui-platform/src/lib/components/sso-settings/sso-settings.component.html new file mode 100644 index 000000000..cfefa383d --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/sso-settings/sso-settings.component.html @@ -0,0 +1,22 @@ +
+
+
+ +
+ + + + +
+ + +
+ + + \ No newline at end of file diff --git a/packages/ee/ui-platform/src/lib/components/sso-settings/sso-settings.component.ts b/packages/ee/ui-platform/src/lib/components/sso-settings/sso-settings.component.ts new file mode 100644 index 000000000..050d03fa9 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/sso-settings/sso-settings.component.ts @@ -0,0 +1,26 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Platform } from '@activepieces/ee-shared'; +import { fadeInUp400ms } from '@activepieces/ui/common'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { FederatedAuthnProviderEnum } from './federated-authn-provider.enum'; + +@Component({ + selector: 'app-sso-settings', + templateUrl: './sso-settings.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + animations: [fadeInUp400ms], +}) +export class SsoSettingsComponent { + platform$: BehaviorSubject; + addDomain$?: Observable; + removeDomain$?: Observable; + FederatedAuthnProviderEnum = FederatedAuthnProviderEnum; + constructor(private route: ActivatedRoute) { + const platform: Platform = this.route.snapshot.data['platform']; + this.platform$ = new BehaviorSubject(platform); + } + platformUpdated(platform: Platform) { + this.platform$.next(platform); + } +} diff --git a/packages/ee/ui-platform/src/lib/components/util.ts b/packages/ee/ui-platform/src/lib/components/util.ts new file mode 100644 index 000000000..13cc877fe --- /dev/null +++ b/packages/ee/ui-platform/src/lib/components/util.ts @@ -0,0 +1,11 @@ +import { Platform } from '@activepieces/ee-shared'; + +export const AtLeastOneLoginMethodMsg = $localize`At least one login method must be enabled`; +export function doesPlatformHaveAtLeastOneLoginMethodEnabled( + platform: Platform +) { + const google = platform.federatedAuthProviders.google; + const github = platform.federatedAuthProviders.github; + const email = platform.emailAuthEnabled; + return google !== undefined || github !== undefined || email; +} diff --git a/packages/ee/ui-platform/src/lib/lib.routes.ts b/packages/ee/ui-platform/src/lib/lib.routes.ts index d75cd4b8a..915ae9b9b 100644 --- a/packages/ee/ui-platform/src/lib/lib.routes.ts +++ b/packages/ee/ui-platform/src/lib/lib.routes.ts @@ -6,6 +6,7 @@ import { PlatformAppearanceComponent } from './pages/platform-appearance/platfor import { PlatformSettingsComponent } from './pages/platform-settings/platform-settings.component'; import { PiecesTableComponent } from './pages/pieces-table/pieces-table.component'; import { TemplatesTableComponent } from './pages/templates-table/templates-table.component'; +import { UsersTableComponent } from './pages/users-table/users-table.component'; export const uiEePlatformRoutes: Route[] = [ { @@ -64,6 +65,13 @@ export const uiEePlatformRoutes: Route[] = [ platform: platformResolver, }, }, + { + path: 'users', + component: UsersTableComponent, + data: { + title: $localize`Users`, + }, + }, ], }, ]; diff --git a/packages/ee/ui-platform/src/lib/pages/pieces-table/pieces-table.component.html b/packages/ee/ui-platform/src/lib/pages/pieces-table/pieces-table.component.html index 161580ac5..e622516f8 100644 --- a/packages/ee/ui-platform/src/lib/pages/pieces-table/pieces-table.component.html +++ b/packages/ee/ui-platform/src/lib/pages/pieces-table/pieces-table.component.html @@ -1,10 +1,8 @@ -
- - - + + + Add Piece -
+
@@ -22,8 +20,6 @@
- - @@ -38,6 +34,13 @@ + + + + +
Piece Package Name + {{ piece.name }} + Version diff --git a/packages/ee/ui-platform/src/lib/pages/pieces-table/pieces-table.component.ts b/packages/ee/ui-platform/src/lib/pages/pieces-table/pieces-table.component.ts index d146d3a36..48c09fc01 100644 --- a/packages/ee/ui-platform/src/lib/pages/pieces-table/pieces-table.component.ts +++ b/packages/ee/ui-platform/src/lib/pages/pieces-table/pieces-table.component.ts @@ -39,7 +39,7 @@ import { changeDetection: ChangeDetectionStrategy.OnPush, }) export class PiecesTableComponent implements OnInit { - displayedColumns = ['displayName', 'version', 'action']; + displayedColumns = ['displayName', 'packageName', 'version', 'action']; title = $localize`Pieces`; saving$?: Observable; platform$!: BehaviorSubject; diff --git a/packages/ee/ui-platform/src/lib/pages/platform-appearance/platform-appearance.component.ts b/packages/ee/ui-platform/src/lib/pages/platform-appearance/platform-appearance.component.ts index 41275e6e6..84ec28ce9 100644 --- a/packages/ee/ui-platform/src/lib/pages/platform-appearance/platform-appearance.component.ts +++ b/packages/ee/ui-platform/src/lib/pages/platform-appearance/platform-appearance.component.ts @@ -11,11 +11,7 @@ import { Validators, } from '@angular/forms'; import { validColorValidator } from 'ngx-colors'; -import { - LocalesEnum, - Platform, - UpdatePlatformRequestBody, -} from '@activepieces/ee-shared'; +import { Platform, UpdatePlatformRequestBody } from '@activepieces/ee-shared'; import { Observable, map, tap } from 'rxjs'; import { AuthenticationService, @@ -23,7 +19,7 @@ import { } from '@activepieces/ui/common'; import { ActivatedRoute } from '@angular/router'; import { localesMap } from '@activepieces/ui/common'; -import { spreadIfDefined } from '@activepieces/shared'; +import { spreadIfDefined, LocalesEnum } from '@activepieces/shared'; interface AppearanceForm { name: FormControl; diff --git a/packages/ee/ui-platform/src/lib/pages/platform-settings/platform-settings.component.html b/packages/ee/ui-platform/src/lib/pages/platform-settings/platform-settings.component.html index ab0925fc7..8ad83ba49 100644 --- a/packages/ee/ui-platform/src/lib/pages/platform-settings/platform-settings.component.html +++ b/packages/ee/ui-platform/src/lib/pages/platform-settings/platform-settings.component.html @@ -11,5 +11,8 @@ + + + \ No newline at end of file diff --git a/packages/ee/ui-platform/src/lib/pages/platform-settings/platform-settings.component.ts b/packages/ee/ui-platform/src/lib/pages/platform-settings/platform-settings.component.ts index 03536e1cf..15cc2274c 100644 --- a/packages/ee/ui-platform/src/lib/pages/platform-settings/platform-settings.component.ts +++ b/packages/ee/ui-platform/src/lib/pages/platform-settings/platform-settings.component.ts @@ -24,12 +24,14 @@ export class PlatformSettingsComponent implements AfterViewInit, OnInit { readonly customDomainTabTitle = $localize`Custom Domains`; readonly privacyAndTermsTabTitle = $localize`Privacy & Terms`; readonly accountManagementEmailTabTitle = $localize`Mail Server`; + readonly tabIndexFragmentMap: { [index: number]: string } = { 0: 'SigningKeys', 1: 'MailServer', 2: 'TermsAndServices', 3: 'CustomDomains', 4: 'ApiKeys', + 5: 'SSO', }; platform!: Platform; diff --git a/packages/ee/ui-platform/src/lib/pages/users-table/users-table.component.html b/packages/ee/ui-platform/src/lib/pages/users-table/users-table.component.html new file mode 100644 index 000000000..65f3d0b19 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/pages/users-table/users-table.component.html @@ -0,0 +1,72 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name{{ user.firstName + ' ' + user.lastName }}Email{{ user.email }}Created{{ user.created | date:"short" }}Updated{{ user.updated | date:"short" }}Status{{ user.status === UserStatus.INACTIVE ? deactivated: active }} + +
+ + +
+ +
+ +
+ + +
+
+ +
+ No users created yet. +
+
+
+ + \ No newline at end of file diff --git a/packages/ee/ui-platform/src/lib/pages/users-table/users-table.component.ts b/packages/ee/ui-platform/src/lib/pages/users-table/users-table.component.ts new file mode 100644 index 000000000..e835c614a --- /dev/null +++ b/packages/ee/ui-platform/src/lib/pages/users-table/users-table.component.ts @@ -0,0 +1,75 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { UsersDataSource } from './users-table.datasource'; +import { + AuthenticationService, + GenericSnackbarTemplateComponent, + PlatformService, +} from '@activepieces/ui/common'; +import { Observable, Subject, startWith, tap } from 'rxjs'; +import { UserResponse, UserStatus } from '@activepieces/shared'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Component({ + selector: 'app-users-table', + templateUrl: './users-table.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UsersTableComponent { + deactivate$?: Observable; + activate$?: Observable; + title = $localize`Users`; + deactivated = $localize`Inactive`; + active = $localize`Active`; + dataSource: UsersDataSource; + UserStatus = UserStatus; + refresh$ = new Subject(); + platformOwnerId: string; + displayedColumns = [ + 'email', + 'name', + 'created', + 'updated', + 'status', + 'action', + ]; + constructor( + private platformService: PlatformService, + private snackBar: MatSnackBar, + private authenticationService: AuthenticationService + ) { + this.platformOwnerId = this.authenticationService.currentUser.id; + this.dataSource = new UsersDataSource( + this.refresh$.asObservable().pipe(startWith(true)), + this.platformService + ); + } + + deactivateUser(user: UserResponse) { + this.deactivate$ = this.platformService + .updateUser(user.id, { status: UserStatus.INACTIVE }) + .pipe( + tap(() => { + this.refresh$.next(true); + this.snackBar.openFromComponent(GenericSnackbarTemplateComponent, { + data: `${user.firstName} ${ + user.lastName + } ${$localize`deactivated`} `, + }); + }) + ); + } + activateUser(user: UserResponse) { + this.activate$ = this.platformService + .updateUser(user.id, { status: UserStatus.ACTIVE }) + .pipe( + tap(() => { + this.refresh$.next(true); + this.snackBar.openFromComponent(GenericSnackbarTemplateComponent, { + data: `${user.firstName} ${ + user.lastName + } ${$localize`activated`} `, + }); + }) + ); + } +} diff --git a/packages/ee/ui-platform/src/lib/pages/users-table/users-table.datasource.ts b/packages/ee/ui-platform/src/lib/pages/users-table/users-table.datasource.ts new file mode 100644 index 000000000..8c3a97836 --- /dev/null +++ b/packages/ee/ui-platform/src/lib/pages/users-table/users-table.datasource.ts @@ -0,0 +1,50 @@ +import { DataSource } from '@angular/cdk/collections'; +import { Observable, BehaviorSubject, tap, switchMap, map } from 'rxjs'; +import { combineLatest } from 'rxjs'; +import { UserResponse } from '@activepieces/shared'; +import { PlatformService } from '@activepieces/ui/common'; + +/** + * Data source for the LogsTable view. This class should + * encapsulate all logic for fetching and manipulating the displayed data + * (including sorting, pagination, and filtering). + */ +export class UsersDataSource extends DataSource { + data: UserResponse[] = []; + isLoading$: BehaviorSubject = new BehaviorSubject(true); + constructor( + private refresh$: Observable, + private platformService: PlatformService + ) { + super(); + } + + /** + * Connect this data source to the table. The table will only update when + * the returned stream emits new items. + * @returns A stream of the items to be rendered. + */ + + connect(): Observable { + return combineLatest([this.refresh$]).pipe( + tap(() => { + this.isLoading$.next(true); + }), + switchMap(() => { + return this.platformService.listUsers().pipe(map((res) => res.data)); + }), + tap((res) => { + this.data = res; + this.isLoading$.next(false); + }) + ); + } + + /** + * Called when the table is being destroyed. Use this function, to clean up + * any open connections or free any held resources that were set up during connect. + */ + disconnect(): void { + //ignore + } +} diff --git a/packages/ee/ui-platform/src/lib/ui-ee-platform.module.ts b/packages/ee/ui-platform/src/lib/ui-ee-platform.module.ts index e58f7ebde..15cb97162 100644 --- a/packages/ee/ui-platform/src/lib/ui-ee-platform.module.ts +++ b/packages/ee/ui-platform/src/lib/ui-ee-platform.module.ts @@ -22,7 +22,13 @@ import { CreateApiKeyDialogComponent } from './components/dialogs/create-api-key import { TemplatesTableComponent } from './pages/templates-table/templates-table.component'; import { CreateOrUpdateTemplateDialogueComponent } from './components/dialogs/create-or-update-template-dialogue/create-or-update-template-dialogue.component'; import { UiFeaturePiecesModule } from '@activepieces/ui/feature-pieces'; - +import { UsersTableComponent } from './pages/users-table/users-table.component'; +import { SsoSettingsComponent } from './components/sso-settings/sso-settings.component'; +import { AddAllowedEmailDomainDialogComponent } from './components/dialogs/add-allowed-email-domain-dialog/add-allowed-email-domain-dialog.component'; +import { AllowedEmailDomainsListComponent } from './components/allowed-email-domains-list/allowed-email-domains-list.component'; +import { ConfigureConfederatedAuthnCardComponent } from './components/configure-confederated-authn-card/configure-confederated-authn-card.component'; +import { EnableFederatedAuthnProviderDialogComponent } from './components/dialogs/enable-federated-authn-provider-dialog/enable-federated-authn-provider-dialog.component'; +import { ConfigureAllowingEmailLoginsCardComponent } from './components/configure-allowing-email-logins-card/configure-allowing-email-logins-card.component'; @NgModule({ imports: [ UiCommonModule, @@ -49,6 +55,13 @@ import { UiFeaturePiecesModule } from '@activepieces/ui/feature-pieces'; TermsAndServicesSettingsComponent, TemplatesTableComponent, CustomDomainTableComponent, + UsersTableComponent, + SsoSettingsComponent, + AddAllowedEmailDomainDialogComponent, + AllowedEmailDomainsListComponent, + ConfigureConfederatedAuthnCardComponent, + EnableFederatedAuthnProviderDialogComponent, + ConfigureAllowingEmailLoginsCardComponent, ], }) export class UiEePlatformModule {} diff --git a/packages/engine/.test.env b/packages/engine/.test.env new file mode 100644 index 000000000..661b59c8f --- /dev/null +++ b/packages/engine/.test.env @@ -0,0 +1 @@ +AP_BASE_CODE_DIRECTORY="packages/engine/test/resources/codes" diff --git a/packages/engine/package.json b/packages/engine/package.json index c0e9b6fa7..eba2c3755 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -1,5 +1,5 @@ { "name": "@activepieces/engine", - "version": "0.3.2", + "version": "0.3.3", "type": "commonjs" } diff --git a/packages/engine/project.json b/packages/engine/project.json index a7396c1c3..c80e7b626 100644 --- a/packages/engine/project.json +++ b/packages/engine/project.json @@ -8,7 +8,6 @@ "executor": "@nx/webpack:webpack", "outputs": ["{options.outputPath}"], "options": { - "statsJson": true, "target": "node", "compiler": "tsc", "outputPath": "dist/packages/engine", @@ -17,13 +16,15 @@ "assets": [], "webpackConfig": "packages/engine/webpack.config.js", "isolatedConfig": true, - "babelUpwardRootMode": true + "babelUpwardRootMode": true, + "statsJson": false }, "configurations": { "production": { "optimization": true, "extractLicenses": true, - "inspect": false + "inspect": false, + "statsJson": false } } }, diff --git a/packages/engine/src/lib/constants.ts b/packages/engine/src/lib/constants.ts deleted file mode 100755 index 19bdc56ca..000000000 --- a/packages/engine/src/lib/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const API_URL = 'http://127.0.0.1:3000/' -export const BASE_CODE_DIRECTORY = process.env.AP_BASE_CODE_DIRECTORY ?? './codes' -export const INPUT_FILE = './input.json' -export const OUTPUT_FILE = './output.json' -export const PIECE_SOURCES = process.env.AP_PIECES_SOURCE ?? 'dev' diff --git a/packages/engine/src/lib/handler/base-executor.ts b/packages/engine/src/lib/handler/base-executor.ts index d3e1ffbb9..d05a995a7 100644 --- a/packages/engine/src/lib/handler/base-executor.ts +++ b/packages/engine/src/lib/handler/base-executor.ts @@ -1,12 +1,11 @@ import { Action } from '@activepieces/shared' import { FlowExecutorContext } from './context/flow-execution-context' -import { EngineConstantData } from './context/engine-constants-data' +import { EngineConstants } from './context/engine-constants' export type BaseExecutor = { handle(request: { action: T executionState: FlowExecutorContext - constants: EngineConstantData + constants: EngineConstants }): Promise } - diff --git a/packages/engine/src/lib/handler/branch-executor.ts b/packages/engine/src/lib/handler/branch-executor.ts index 851d3f9a2..ea8ba7e56 100644 --- a/packages/engine/src/lib/handler/branch-executor.ts +++ b/packages/engine/src/lib/handler/branch-executor.ts @@ -2,7 +2,7 @@ import { BranchAction, BranchActionSettings, BranchCondition, BranchOperator, Br import { BaseExecutor } from './base-executor' import { ExecutionVerdict, FlowExecutorContext } from './context/flow-execution-context' import { flowExecutor } from './flow-executor' -import { EngineConstantData } from './context/engine-constants-data' +import { EngineConstants } from './context/engine-constants' export const branchExecutor: BaseExecutor = { async handle({ @@ -12,7 +12,7 @@ export const branchExecutor: BaseExecutor = { }: { action: BranchAction executionState: FlowExecutorContext - constants: EngineConstantData + constants: EngineConstants }) { const { censoredInput, resolvedInput } = await constants.variableService.resolve({ unresolvedInput: action.settings, diff --git a/packages/engine/src/lib/handler/code-executor.ts b/packages/engine/src/lib/handler/code-executor.ts index 5bc7e6989..f489a3a32 100644 --- a/packages/engine/src/lib/handler/code-executor.ts +++ b/packages/engine/src/lib/handler/code-executor.ts @@ -1,7 +1,7 @@ import { ActionType, CodeAction, GenricStepOutput, StepOutputStatus } from '@activepieces/shared' import { BaseExecutor } from './base-executor' import { ExecutionVerdict, FlowExecutorContext } from './context/flow-execution-context' -import { EngineConstantData } from './context/engine-constants-data' +import { EngineConstants } from './context/engine-constants' type CodePieceModule = { code(params: unknown): Promise @@ -15,7 +15,7 @@ export const codeExecutor: BaseExecutor = { }: { action: CodeAction executionState: FlowExecutorContext - constants: EngineConstantData + constants: EngineConstants }) { if (executionState.isCompleted({ stepName: action.name })) { return executionState @@ -33,7 +33,7 @@ export const codeExecutor: BaseExecutor = { const artifactPath = `${constants.baseCodeDirectory}/${action.name}/index.js` const codePieceModule: CodePieceModule = await import(artifactPath) const output = await codePieceModule.code(resolvedInput) - return executionState.upsertStep(action.name, stepOutput.setOutput(output)) + return executionState.upsertStep(action.name, stepOutput.setOutput(output)).increaseTask() } catch (e) { console.error(e) @@ -42,4 +42,4 @@ export const codeExecutor: BaseExecutor = { .setVerdict(ExecutionVerdict.FAILED, undefined) } }, -} \ No newline at end of file +} diff --git a/packages/engine/src/lib/handler/context/engine-constants-data.ts b/packages/engine/src/lib/handler/context/engine-constants-data.ts deleted file mode 100644 index 490369a32..000000000 --- a/packages/engine/src/lib/handler/context/engine-constants-data.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import { ExecutionType, ProjectId } from '@activepieces/shared' -import { VariableService } from '../../services/variable-service' - -export type EngineConstantData = { - flowRunId: string - serverUrl: string - apiUrl: string - executionType: ExecutionType - workerToken: string - projectId: ProjectId - flowId: string - variableService: VariableService - resumePayload?: unknown - baseCodeDirectory: string - piecesSource: string - testSingleStepMode: boolean - filesServiceType: 'local' | 'db' -} diff --git a/packages/engine/src/lib/handler/context/engine-constants.ts b/packages/engine/src/lib/handler/context/engine-constants.ts new file mode 100644 index 000000000..52665d231 --- /dev/null +++ b/packages/engine/src/lib/handler/context/engine-constants.ts @@ -0,0 +1,111 @@ +import { ExecuteFlowOperation, ExecuteStepOperation, ExecuteTriggerOperation, ExecutionType, Project, ProjectId, TriggerHookType } from '@activepieces/shared' +import { VariableService } from '../../services/variable-service' + +export class EngineConstants { + public static readonly API_URL = 'http://127.0.0.1:3000/' + public static readonly BASE_CODE_DIRECTORY = process.env.AP_BASE_CODE_DIRECTORY ?? './codes' + public static readonly INPUT_FILE = './input.json' + public static readonly OUTPUT_FILE = './output.json' + public static readonly PIECE_SOURCES = process.env.AP_PIECES_SOURCE ?? 'FILE' + + private project: Project | null = null + + public get apiUrl(): string { + return EngineConstants.API_URL + } + + public get baseCodeDirectory(): string { + return EngineConstants.BASE_CODE_DIRECTORY + } + + public get piecesSource(): string { + return EngineConstants.PIECE_SOURCES + } + + public constructor( + public readonly flowId: string, + public readonly flowRunId: string, + public readonly serverUrl: string, + public readonly executionType: ExecutionType, + public readonly workerToken: string, + public readonly projectId: ProjectId, + public readonly variableService: VariableService, + public readonly testSingleStepMode: boolean, + public readonly filesServiceType: 'local' | 'db', + public readonly resumePayload?: unknown, + ) {} + + public static fromExecuteFlowInput(input: ExecuteFlowOperation): EngineConstants { + return new EngineConstants( + input.flowVersion.flowId, + input.flowRunId, + input.serverUrl, + input.executionType, + input.workerToken, + input.projectId, + new VariableService({ + projectId: input.projectId, + workerToken: input.workerToken, + }), + false, + 'local', + input.executionType === ExecutionType.RESUME ? input.resumePayload : undefined, + ) + } + + public static fromExecuteStepInput(input: ExecuteStepOperation): EngineConstants { + return new EngineConstants( + input.flowVersion.flowId, + 'test-run', + input.serverUrl, + ExecutionType.BEGIN, + input.workerToken, + input.projectId, + new VariableService({ + projectId: input.projectId, + workerToken: input.workerToken, + }), + true, + 'db', + ) + } + + public static fromExecuteTriggerInput(input: ExecuteTriggerOperation): EngineConstants { + return new EngineConstants( + input.flowVersion.flowId, + 'execute-trigger', + input.serverUrl, + ExecutionType.BEGIN, + input.workerToken, + input.projectId, + new VariableService({ + projectId: input.projectId, + workerToken: input.workerToken, + }), + true, + 'db', + ) + } + + private async getProject(): Promise { + if (this.project) { + return this.project + } + + const getWorkerProjectEndpoint = `${EngineConstants.API_URL}v1/worker/project` + + const response = await fetch(getWorkerProjectEndpoint, { + headers: { + Authorization: `Bearer ${this.workerToken}`, + }, + }) + + this.project = await response.json() as Project + return this.project + } + + public externalProjectId = async (): Promise => { + const project = await this.getProject() + return project.externalId + } +} diff --git a/packages/engine/src/lib/handler/context/flow-execution-context.ts b/packages/engine/src/lib/handler/context/flow-execution-context.ts index 428f5c2e8..515e86f5d 100644 --- a/packages/engine/src/lib/handler/context/flow-execution-context.ts +++ b/packages/engine/src/lib/handler/context/flow-execution-context.ts @@ -68,6 +68,13 @@ export class FlowExecutorContext { }) } + public increaseTask(tasks = 1): FlowExecutorContext { + return new FlowExecutorContext({ + ...this, + tasks: this.tasks + tasks, + }) + } + public upsertStep(stepName: string, stepOutput: StepOutput): FlowExecutorContext { const steps = { ...this.steps, @@ -77,7 +84,7 @@ export class FlowExecutorContext { return new FlowExecutorContext({ ...this, - tasks: this.tasks + 1, + tasks: this.tasks, currentState: { ...this.currentState, [stepName]: stepOutput.output, diff --git a/packages/engine/src/lib/handler/flow-executor.ts b/packages/engine/src/lib/handler/flow-executor.ts index ae386d43d..8bcd5b3de 100644 --- a/packages/engine/src/lib/handler/flow-executor.ts +++ b/packages/engine/src/lib/handler/flow-executor.ts @@ -5,7 +5,7 @@ import { branchExecutor } from './branch-executor' import { BaseExecutor } from './base-executor' import { loopExecutor } from './loop-executor' import { pieceExecutor } from './piece-executor' -import { EngineConstantData } from './context/engine-constants-data' +import { EngineConstants } from './context/engine-constants' const executeFunction: Record> = { [ActionType.CODE]: codeExecutor, @@ -25,7 +25,7 @@ export const flowExecutor = { async execute({ action, constants, executionState }: { action: Action executionState: FlowExecutorContext - constants: EngineConstantData + constants: EngineConstants }): Promise { const startTime = new Date().getMilliseconds() let flowExecutionContext = executionState diff --git a/packages/engine/src/lib/handler/loop-executor.ts b/packages/engine/src/lib/handler/loop-executor.ts index f55807a5c..50f05e7fd 100644 --- a/packages/engine/src/lib/handler/loop-executor.ts +++ b/packages/engine/src/lib/handler/loop-executor.ts @@ -2,7 +2,7 @@ import { LoopOnItemsAction, LoopStepOutput, isNil } from '@activepieces/shared' import { BaseExecutor } from './base-executor' import { ExecutionVerdict, FlowExecutorContext } from './context/flow-execution-context' import { flowExecutor } from './flow-executor' -import { EngineConstantData } from './context/engine-constants-data' +import { EngineConstants } from './context/engine-constants' type LoopOnActionResolvedSettings = { items: readonly unknown[] @@ -16,7 +16,7 @@ export const loopExecutor: BaseExecutor = { }: { action: LoopOnItemsAction executionState: FlowExecutorContext - constants: EngineConstantData + constants: EngineConstants }) { const { resolvedInput, censoredInput } = await constants.variableService.resolve({ unresolvedInput: { @@ -44,12 +44,12 @@ export const loopExecutor: BaseExecutor = { executionState: newExecutionContext, constants, }) - } - + } + if (newExecutionContext.verdict !== ExecutionVerdict.RUNNING) { return newExecutionContext } - + newExecutionContext = newExecutionContext.setCurrentPath(newExecutionContext.currentPath.removeLast()) if (constants.testSingleStepMode) { break @@ -57,4 +57,4 @@ export const loopExecutor: BaseExecutor = { } return newExecutionContext }, -} \ No newline at end of file +} diff --git a/packages/engine/src/lib/handler/piece-executor.ts b/packages/engine/src/lib/handler/piece-executor.ts index 76a845fb7..c4d3f5a1e 100644 --- a/packages/engine/src/lib/handler/piece-executor.ts +++ b/packages/engine/src/lib/handler/piece-executor.ts @@ -6,7 +6,7 @@ import { ActionContext, ConnectionsManager, PauseHook, PauseHookParams, PiecePro import { createContextStore } from '../services/storage.service' import { createFilesService } from '../services/files.service' import { createConnectionService } from '../services/connections.service' -import { EngineConstantData } from './context/engine-constants-data' +import { EngineConstants } from './context/engine-constants' import { pieceLoader } from '../helper/piece-loader' import { utils } from '../utils' @@ -20,7 +20,7 @@ export const pieceExecutor: BaseExecutor = { }: { action: PieceAction executionState: FlowExecutorContext - constants: EngineConstantData + constants: EngineConstants }) { if (executionState.isCompleted({ stepName: action.name })) { return executionState @@ -97,6 +97,10 @@ export const pieceExecutor: BaseExecutor = { pause: createPauseHook(hookResponse), }, resumePayload: constants.resumePayload, + project: { + id: constants.projectId, + externalId: constants.externalProjectId, + }, } const output = await pieceAction.run(context) const newExecutionContext = executionState.addTags(hookResponse.tags) @@ -106,7 +110,7 @@ export const pieceExecutor: BaseExecutor = { return newExecutionContext.upsertStep(action.name, stepOutput.setOutput(output)).setVerdict(ExecutionVerdict.SUCCEEDED, { reason: ExecutionOutputStatus.STOPPED, stopResponse: hookResponse.stopResponse.response, - }) + }).increaseTask() } if (hookResponse.paused) { assertNotNullOrUndefined(hookResponse.pauseResponse, 'pauseResponse') @@ -118,10 +122,10 @@ export const pieceExecutor: BaseExecutor = { }) } - return newExecutionContext.upsertStep(action.name, stepOutput.setOutput(output)).setVerdict(ExecutionVerdict.RUNNING, undefined) + return newExecutionContext.upsertStep(action.name, stepOutput.setOutput(output)).increaseTask().setVerdict(ExecutionVerdict.RUNNING, undefined) } catch (e) { - const errorMessage = await utils.tryParseJson((e as Error).message) + const errorMessage = await utils.tryParseJson((e as Error).message) console.error(errorMessage) return executionState .upsertStep(action.name, stepOutput.setStatus(StepOutputStatus.FAILED).setErrorMessage(errorMessage)) diff --git a/packages/engine/src/lib/helper/piece-helper.ts b/packages/engine/src/lib/helper/piece-helper.ts index 2a99285fd..94a9d7700 100644 --- a/packages/engine/src/lib/helper/piece-helper.ts +++ b/packages/engine/src/lib/helper/piece-helper.ts @@ -17,12 +17,11 @@ import { ExecuteExtractPieceMetadata, ExecutePropsOptions, } from '@activepieces/shared' -import { API_URL } from '../constants' +import { EngineConstants } from '../handler/context/engine-constants' import { FlowExecutorContext } from '../handler/context/flow-execution-context' import { pieceLoader } from './piece-loader' import { variableService } from '../services/variable-service' - export const pieceHelper = { async executeProps({ params, piecesSource }: { params: ExecutePropsOptions, piecesSource: string }) { const property = await pieceLoader.getPropOrThrow({ @@ -43,7 +42,7 @@ export const pieceHelper = { const ctx = { server: { token: params.workerToken, - apiUrl: API_URL, + apiUrl: EngineConstants.API_URL, publicUrl: params.serverUrl, }, } diff --git a/packages/engine/src/lib/helper/piece-loader.ts b/packages/engine/src/lib/helper/piece-loader.ts index 7287d35f7..054f7be96 100644 --- a/packages/engine/src/lib/helper/piece-loader.ts +++ b/packages/engine/src/lib/helper/piece-loader.ts @@ -101,7 +101,7 @@ const getPackageAlias = ({ pieceName, pieceVersion, piecesSource }: { piecesSource: string pieceVersion: string }) => { - if (piecesSource === 'FILE') { + if (piecesSource.trim() === 'FILE') { return pieceName } diff --git a/packages/engine/src/lib/helper/trigger-helper.ts b/packages/engine/src/lib/helper/trigger-helper.ts index ef9267f91..2ec38ece2 100644 --- a/packages/engine/src/lib/helper/trigger-helper.ts +++ b/packages/engine/src/lib/helper/trigger-helper.ts @@ -6,6 +6,7 @@ import { PiecePropertyMap, StaticPropsValue, TriggerStrategy } from '@activepiec import { createFilesService } from '../services/files.service' import { FlowExecutorContext } from '../handler/context/flow-execution-context' import { pieceLoader } from './piece-loader' +import { EngineConstants } from '../handler/context/engine-constants' type Listener = { events: string[] @@ -14,10 +15,10 @@ type Listener = { } export const triggerHelper = { - async executeTrigger({ params, piecesSource }: { piecesSource: string, params: ExecuteTriggerOperation }): Promise> { + async executeTrigger({ params, constants }: ExecuteTriggerParams): Promise> { const { pieceName, pieceVersion, triggerName, input } = (params.flowVersion.trigger as PieceTrigger).settings - const piece = await pieceLoader.loadPieceOrThrow({ pieceName, pieceVersion, piecesSource }) + const piece = await pieceLoader.loadPieceOrThrow({ pieceName, pieceVersion, piecesSource: constants.piecesSource }) const trigger = piece.getTrigger(triggerName) if (trigger === undefined) { @@ -68,6 +69,10 @@ export const triggerHelper = { auth: processedInput[AUTHENTICATION_PROPERTY_NAME], propsValue: processedInput, payload: params.triggerPayload ?? {}, + project: { + id: params.projectId, + externalId: constants.externalProjectId, + }, } switch (params.hookType) { case TriggerHookType.ON_DISABLE: @@ -182,3 +187,8 @@ export const triggerHelper = { } }, } + +type ExecuteTriggerParams = { + params: ExecuteTriggerOperation + constants: EngineConstants +} diff --git a/packages/engine/src/lib/services/connections.service.ts b/packages/engine/src/lib/services/connections.service.ts index ba5a49c82..59c7d2ff1 100644 --- a/packages/engine/src/lib/services/connections.service.ts +++ b/packages/engine/src/lib/services/connections.service.ts @@ -1,10 +1,10 @@ -import { API_URL } from '../constants' +import { EngineConstants } from '../handler/context/engine-constants' import { AppConnection, AppConnectionType, CloudOAuth2ConnectionValue, BasicAuthConnectionValue, OAuth2ConnectionValueWithApp } from '@activepieces/shared' export const createConnectionService = ({ projectId, workerToken }: { projectId: string, workerToken: string }) => { return { async obtain(connectionName: string): Promise> { - const url = API_URL + `v1/worker/app-connections/${encodeURIComponent(connectionName)}?projectId=${projectId}` + const url = `${EngineConstants.API_URL}v1/worker/app-connections/${encodeURIComponent(connectionName)}?projectId=${projectId}` try { const response = await fetch(url, { method: 'GET', diff --git a/packages/engine/src/lib/services/files.service.ts b/packages/engine/src/lib/services/files.service.ts index 1bc7497c5..df90ba021 100644 --- a/packages/engine/src/lib/services/files.service.ts +++ b/packages/engine/src/lib/services/files.service.ts @@ -1,11 +1,13 @@ import fs from 'fs/promises' import { ApFile } from '@activepieces/pieces-framework' import { isString } from '@activepieces/shared' -import { API_URL } from '../constants' +import { EngineConstants } from '../handler/context/engine-constants' const DB_PREFIX_URL = 'db://' const FILE_PREFIX_URL = 'file://' const MEMORY_PREFIX_URL = 'memory://' +const MAXIMUM = 4 * 1024 * 1024 +const MAXIMUM_MB = MAXIMUM / 1024 / 1024 export type DefaultFileSystem = 'db' | 'local' | 'memory' @@ -79,6 +81,7 @@ async function readMemoryFile(absolutePath: string): Promise { } } + async function writeDbFile({ stepName, flowId, fileName, data, workerToken }: { stepName: string, flowId: string, fileName: string, data: Buffer, workerToken: string }): Promise { const formData = new FormData() formData.append('stepName', stepName) @@ -86,7 +89,15 @@ async function writeDbFile({ stepName, flowId, fileName, data, workerToken }: { formData.append('flowId', flowId) formData.append('file', new Blob([data], { type: 'application/octet-stream' })) - const response = await fetch(API_URL + 'v1/step-files', { + if (data.length > MAXIMUM) { + throw new Error(JSON.stringify({ + message: 'File size is larger than maximum supported size in test step mode, please use test flow instead of step as a workaround', + currentFileSize: `${(data.length / 1024 / 1024).toFixed(2)} MB`, + maximumSupportSize: `${MAXIMUM_MB.toFixed(2)} MB`, + })) + } + + const response = await fetch(EngineConstants.API_URL + 'v1/step-files', { method: 'POST', headers: { Authorization: 'Bearer ' + workerToken, @@ -97,13 +108,14 @@ async function writeDbFile({ stepName, flowId, fileName, data, workerToken }: { if (!response.ok) { throw new Error('Failed to store entry ' + response.body) } + const result = await response.json() return result.url } async function readDbFile({ workerToken, absolutePath }: { workerToken: string, absolutePath: string }): Promise { const fileId = absolutePath.replace(DB_PREFIX_URL, '') - const response = await fetch(API_URL + `v1/step-files/${encodeURIComponent(fileId)}`, { + const response = await fetch(`${EngineConstants.API_URL}v1/step-files/${encodeURIComponent(fileId)}`, { method: 'GET', headers: { 'Content-Type': 'application/json', diff --git a/packages/engine/src/lib/services/storage.service.ts b/packages/engine/src/lib/services/storage.service.ts index 2bda6f108..adf0a9a27 100644 --- a/packages/engine/src/lib/services/storage.service.ts +++ b/packages/engine/src/lib/services/storage.service.ts @@ -1,11 +1,11 @@ import { Store, StoreScope } from '@activepieces/pieces-framework' import { DeletStoreEntryRequest, FlowId, PutStoreEntryRequest, StoreEntry } from '@activepieces/shared' -import { API_URL } from '../constants' +import { EngineConstants } from '../handler/context/engine-constants' export const createStorageService = ({ workerToken }: { workerToken: string }) => { return { async get(key: string): Promise { - const response = await fetch(API_URL + 'v1/store-entries?key=' + encodeURIComponent(key), { + const response = await fetch(`${EngineConstants.API_URL}v1/store-entries?key=${encodeURIComponent(key)}`, { headers: { Authorization: 'Bearer ' + workerToken, }, @@ -16,7 +16,7 @@ export const createStorageService = ({ workerToken }: { workerToken: string }) = return (await response.json()) ?? null }, async put(request: PutStoreEntryRequest): Promise { - const response = await fetch(API_URL + 'v1/store-entries', { + const response = await fetch(`${EngineConstants.API_URL}v1/store-entries`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -30,7 +30,7 @@ export const createStorageService = ({ workerToken }: { workerToken: string }) = return (await response.json()) ?? null }, async delete(request: DeletStoreEntryRequest): Promise { - const response = await fetch(API_URL + 'v1/store-entries?key=' + encodeURIComponent(request.key), { + const response = await fetch(`${EngineConstants.API_URL}v1/store-entries?key=${encodeURIComponent(request.key)}`, { method: 'DELETE', headers: { Authorization: 'Bearer ' + workerToken, diff --git a/packages/engine/src/main.ts b/packages/engine/src/main.ts index efde3272b..fd6077906 100755 --- a/packages/engine/src/main.ts +++ b/packages/engine/src/main.ts @@ -15,7 +15,7 @@ import { ExecuteActionResponse, EngineResponse, GenricStepOutput, - ExcuteStepOperation, + ExecuteStepOperation, flowHelper, Action, ActionType, @@ -26,32 +26,14 @@ import { triggerHelper } from './lib/helper/trigger-helper' import { utils } from './lib/utils' import { flowExecutor } from './lib/handler/flow-executor' import { ExecutionVerdict, FlowExecutorContext } from './lib/handler/context/flow-execution-context' -import { BASE_CODE_DIRECTORY, INPUT_FILE, OUTPUT_FILE, PIECE_SOURCES } from './lib/constants' +import { EngineConstants } from './lib/handler/context/engine-constants' import { testExecutionContext } from './lib/handler/context/test-execution-context' -import { VariableService } from './lib/services/variable-service' const executeFlow = async (input: ExecuteFlowOperation, context: FlowExecutorContext): Promise> => { const output = await flowExecutor.execute({ action: input.flowVersion.trigger.nextAction, executionState: context, - constants: { - flowId: input.flowVersion.flowId, - flowRunId: input.flowRunId, - executionType: input.executionType, - serverUrl: input.serverUrl, - testSingleStepMode: false, - apiUrl: input.serverUrl, - projectId: input.projectId, - workerToken: input.workerToken, - variableService: new VariableService({ - projectId: input.projectId, - workerToken: input.workerToken, - }), - filesServiceType: 'local', - resumePayload: input.executionType === ExecutionType.RESUME ? input.resumePayload : undefined, - piecesSource: PIECE_SOURCES, - baseCodeDirectory: BASE_CODE_DIRECTORY, - }, + constants: EngineConstants.fromExecuteFlowInput(input), }) return { status: EngineResponseStatus.OK, @@ -60,7 +42,7 @@ const executeFlow = async (input: ExecuteFlowOperation, context: FlowExecutorCon } -async function executeStep(input: ExcuteStepOperation): Promise { +async function executeStep(input: ExecuteStepOperation): Promise { const step = flowHelper.getStep(input.flowVersion, input.stepName) as Action | undefined if (isNil(step) || !Object.values(ActionType).includes(step.type)) { throw new Error('Step not found or not supported') @@ -73,23 +55,7 @@ async function executeStep(input: ExcuteStepOperation): Promise => { try { @@ -104,10 +89,10 @@ const execute = async (): Promise => { switch (operationType) { case EngineOperationType.EXTRACT_PIECE_METADATA: { - const input: ExecuteExtractPieceMetadata = await utils.parseJsonFile(INPUT_FILE) + const input: ExecuteExtractPieceMetadata = await utils.parseJsonFile(EngineConstants.INPUT_FILE) const output = await pieceHelper.extractPieceMetadata({ params: input, - piecesSource: PIECE_SOURCES, + piecesSource: EngineConstants.PIECE_SOURCES, }) await writeOutput({ status: EngineResponseStatus.OK, @@ -116,21 +101,17 @@ const execute = async (): Promise => { break } case EngineOperationType.EXECUTE_FLOW: { - const input: ExecuteFlowOperation = await utils.parseJsonFile(INPUT_FILE) - const flowExecutorContext = FlowExecutorContext.empty().upsertStep(input.flowVersion.trigger.name, GenricStepOutput.create({ - type: input.flowVersion.trigger.type, - status: StepOutputStatus.SUCCEEDED, - input: {}, - }).setOutput(input.triggerPayload)) + const input: ExecuteFlowOperation = await utils.parseJsonFile(EngineConstants.INPUT_FILE) + const flowExecutorContext = getFlowExecutionState(input) const output = await executeFlow(input, flowExecutorContext) await writeOutput(output) break } case EngineOperationType.EXECUTE_PROPERTY: { - const input: ExecutePropsOptions = await utils.parseJsonFile(INPUT_FILE) + const input: ExecutePropsOptions = await utils.parseJsonFile(EngineConstants.INPUT_FILE) const output = await pieceHelper.executeProps({ params: input, - piecesSource: PIECE_SOURCES, + piecesSource: EngineConstants.PIECE_SOURCES, }) await writeOutput({ status: EngineResponseStatus.OK, @@ -139,11 +120,11 @@ const execute = async (): Promise => { break } case EngineOperationType.EXECUTE_TRIGGER_HOOK: { - const input: ExecuteTriggerOperation = await utils.parseJsonFile(INPUT_FILE) + const input: ExecuteTriggerOperation = await utils.parseJsonFile(EngineConstants.INPUT_FILE) const output = await triggerHelper.executeTrigger({ params: input, - piecesSource: PIECE_SOURCES, + constants: EngineConstants.fromExecuteTriggerInput(input), }) await writeOutput({ status: EngineResponseStatus.OK, @@ -152,7 +133,7 @@ const execute = async (): Promise => { break } case EngineOperationType.EXECUTE_STEP: { - const input: ExcuteStepOperation = await utils.parseJsonFile(INPUT_FILE) + const input: ExecuteStepOperation = await utils.parseJsonFile(EngineConstants.INPUT_FILE) const output = await executeStep(input) await writeOutput({ status: EngineResponseStatus.OK, @@ -161,10 +142,10 @@ const execute = async (): Promise => { break } case EngineOperationType.EXECUTE_VALIDATE_AUTH: { - const input: ExecuteValidateAuthOperation = await utils.parseJsonFile(INPUT_FILE) + const input: ExecuteValidateAuthOperation = await utils.parseJsonFile(EngineConstants.INPUT_FILE) const output = await pieceHelper.executeValidateAuth({ params: input, - piecesSource: PIECE_SOURCES, + piecesSource: EngineConstants.PIECE_SOURCES, }) await writeOutput({ @@ -174,7 +155,7 @@ const execute = async (): Promise => { break } case EngineOperationType.EXECUTE_TEST_FLOW: { - const input: EngineTestOperation = await utils.parseJsonFile(INPUT_FILE) + const input: EngineTestOperation = await utils.parseJsonFile(EngineConstants.INPUT_FILE) const testExecutionState = await testExecutionContext.stateFromFlowVersion({ flowVersion: input.sourceFlowVersion, projectId: input.projectId, @@ -202,5 +183,5 @@ execute() .catch(e => console.error(e)) async function writeOutput(result: EngineResponse): Promise { - await utils.writeToJsonFile(OUTPUT_FILE, result) -} \ No newline at end of file + await utils.writeToJsonFile(EngineConstants.OUTPUT_FILE, result) +} diff --git a/packages/engine/test/handler/flow-branching-routing.test.ts b/packages/engine/test/handler/flow-branching-routing.test.ts index 36fd4d2b0..49060f38e 100644 --- a/packages/engine/test/handler/flow-branching-routing.test.ts +++ b/packages/engine/test/handler/flow-branching-routing.test.ts @@ -1,5 +1,5 @@ import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context' -import { EXECUTE_CONSTANTS, buildActionWithOneCondition, buildCodeAction } from './test-helper' +import { buildActionWithOneCondition, buildCodeAction, generateMockEngineConstants } from './test-helper' import { flowExecutor } from '../../src/lib/handler/flow-executor' import { BranchAction, BranchCondition, BranchOperator } from '@activepieces/shared' @@ -37,7 +37,7 @@ describe('flow with branching', () => { }), executionState: FlowExecutorContext.empty(), - constants: EXECUTE_CONSTANTS, + constants: generateMockEngineConstants(), }) expect(result.verdict).toBe(ExecutionVerdict.RUNNING) @@ -57,7 +57,7 @@ describe('flow with branching', () => { }), executionState: FlowExecutorContext.empty(), - constants: EXECUTE_CONSTANTS, + constants: generateMockEngineConstants(), }) expect(result.verdict).toBe(ExecutionVerdict.RUNNING) @@ -66,4 +66,4 @@ describe('flow with branching', () => { 'failure': 'true', }) }) -}) \ No newline at end of file +}) diff --git a/packages/engine/test/handler/flow-branching.test.ts b/packages/engine/test/handler/flow-branching.test.ts index 6a4eeda28..edc69bf52 100644 --- a/packages/engine/test/handler/flow-branching.test.ts +++ b/packages/engine/test/handler/flow-branching.test.ts @@ -1,6 +1,6 @@ import { BranchCondition, BranchOperator } from '@activepieces/shared' import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context' -import { EXECUTE_CONSTANTS, buildActionWithOneCondition } from './test-helper' +import { buildActionWithOneCondition, generateMockEngineConstants } from './test-helper' import { flowExecutor } from '../../src/lib/handler/flow-executor' function executeBranchActionWithOneCondition(condition: BranchCondition): Promise { @@ -9,7 +9,7 @@ function executeBranchActionWithOneCondition(condition: BranchCondition): Promis condition, }), executionState: FlowExecutorContext.empty(), - constants: EXECUTE_CONSTANTS, + constants: generateMockEngineConstants(), }) } describe('flow with branching different conditions', () => { @@ -306,4 +306,4 @@ describe('flow with branching different conditions', () => { }) }) -}) \ No newline at end of file +}) diff --git a/packages/engine/test/handler/flow-codes.test.ts b/packages/engine/test/handler/flow-codes.test.ts index b4e38350c..2bd9d5bc0 100644 --- a/packages/engine/test/handler/flow-codes.test.ts +++ b/packages/engine/test/handler/flow-codes.test.ts @@ -1,6 +1,6 @@ import { codeExecutor } from '../../src/lib/handler/code-executor' import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context' -import { EXECUTE_CONSTANTS, buildCodeAction } from './test-helper' +import { buildCodeAction, generateMockEngineConstants } from './test-helper' describe('codeExecutor', () => { @@ -11,7 +11,7 @@ describe('codeExecutor', () => { input: { 'key': '{{ 1 + 2 }}', }, - }), executionState: FlowExecutorContext.empty(), constants: EXECUTE_CONSTANTS, + }), executionState: FlowExecutorContext.empty(), constants: generateMockEngineConstants(), }) expect(result.verdict).toBe(ExecutionVerdict.RUNNING) expect(result.steps.echo_step.output).toEqual({ 'key': 3 }) @@ -22,11 +22,11 @@ describe('codeExecutor', () => { action: buildCodeAction({ name: 'runtime', input: {}, - }), executionState: FlowExecutorContext.empty(), constants: EXECUTE_CONSTANTS, + }), executionState: FlowExecutorContext.empty(), constants: generateMockEngineConstants(), }) expect(result.verdict).toBe(ExecutionVerdict.FAILED) expect(result.steps.runtime.status).toEqual('FAILED') expect(result.steps.runtime.errorMessage).toEqual('Custom Runtime Error') }) -}) \ No newline at end of file +}) diff --git a/packages/engine/test/handler/flow-looping.test.ts b/packages/engine/test/handler/flow-looping.test.ts index fdac5facc..dd917f18c 100644 --- a/packages/engine/test/handler/flow-looping.test.ts +++ b/packages/engine/test/handler/flow-looping.test.ts @@ -1,5 +1,5 @@ import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context' -import { EXECUTE_CONSTANTS, buildCodeAction, buildSimpleLoopAction } from './test-helper' +import { buildCodeAction, buildSimpleLoopAction, generateMockEngineConstants } from './test-helper' import { flowExecutor } from '../../src/lib/handler/flow-executor' import { LoopStepOutput } from '@activepieces/shared' @@ -20,7 +20,7 @@ describe('flow with looping', () => { firstLoopAction: codeAction, }), executionState: FlowExecutorContext.empty(), - constants: EXECUTE_CONSTANTS, + constants: generateMockEngineConstants(), }) const loopOut = result.steps.loop as LoopStepOutput @@ -48,7 +48,7 @@ describe('flow with looping', () => { const result = await flowExecutor.execute({ action: generateArray, executionState: FlowExecutorContext.empty(), - constants: EXECUTE_CONSTANTS, + constants: generateMockEngineConstants(), }) const loopOut = result.steps.loop as LoopStepOutput @@ -58,4 +58,4 @@ describe('flow with looping', () => { expect(loopOut.output?.item).toBe(4) }) -}) \ No newline at end of file +}) diff --git a/packages/engine/test/handler/flow-piece.test.ts b/packages/engine/test/handler/flow-piece.test.ts index de6ec45d4..bbdabf584 100644 --- a/packages/engine/test/handler/flow-piece.test.ts +++ b/packages/engine/test/handler/flow-piece.test.ts @@ -1,6 +1,6 @@ import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context' import { pieceExecutor } from '../../src/lib/handler/piece-executor' -import { EXECUTE_CONSTANTS, buildPieceAction } from './test-helper' +import { buildPieceAction, generateMockEngineConstants } from './test-helper' describe('pieceExecutor', () => { @@ -15,7 +15,7 @@ describe('pieceExecutor', () => { 'key': '{{ 1 + 2 }}', }, }, - }), executionState: FlowExecutorContext.empty(), constants: EXECUTE_CONSTANTS, + }), executionState: FlowExecutorContext.empty(), constants: generateMockEngineConstants(), }) expect(result.verdict).toBe(ExecutionVerdict.RUNNING) expect(result.steps.data_mapper.output).toEqual({ 'key': 3 }) @@ -34,7 +34,7 @@ describe('pieceExecutor', () => { 'failsafe': false, 'queryParams': {}, }, - }), executionState: FlowExecutorContext.empty(), constants: EXECUTE_CONSTANTS, + }), executionState: FlowExecutorContext.empty(), constants: generateMockEngineConstants(), }) expect(result.verdict).toBe(ExecutionVerdict.FAILED) expect(result.steps.send_http.status).toBe('FAILED') @@ -48,4 +48,4 @@ describe('pieceExecutor', () => { }) -}) \ No newline at end of file +}) diff --git a/packages/engine/test/handler/flow-rerun.test.ts b/packages/engine/test/handler/flow-rerun.test.ts new file mode 100644 index 000000000..3685039fc --- /dev/null +++ b/packages/engine/test/handler/flow-rerun.test.ts @@ -0,0 +1,58 @@ +import { flowExecutor } from '../../src/lib/handler/flow-executor' +import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context' +import { buildPieceAction, generateMockEngineConstants } from './test-helper' +import { ExecutionType } from '@activepieces/shared' + +const failedHttpAction = buildPieceAction({ + name: 'send_http', + pieceName: '@activepieces/piece-http', + actionName: 'send_request', + input: { + 'url': 'https://cloud.activepieces.com/api/v1/asd', + 'method': 'GET', + 'headers': {}, + 'failsafe': false, + 'queryParams': {}, + }, +}) + +const successHttpAction = buildPieceAction({ + name: 'send_http', + pieceName: '@activepieces/piece-http', + actionName: 'send_request', + input: { + 'url': 'https://cloud.activepieces.com/api/v1/pieces', + 'method': 'GET', + 'headers': {}, + 'failsafe': false, + 'queryParams': {}, + }, +}) + +describe('flow retry', () => { + const context = FlowExecutorContext.empty() + it('should retry entire flow', async () => { + const failedResult = await flowExecutor.execute({ + action: failedHttpAction, executionState: context, constants: generateMockEngineConstants(), + }) + const retryEntireFlow = await flowExecutor.execute({ + action: successHttpAction, executionState: context, constants: generateMockEngineConstants(), + }) + expect(failedResult.verdict).toBe(ExecutionVerdict.FAILED) + expect(retryEntireFlow.verdict).toBe(ExecutionVerdict.RUNNING) + }) + + it('should retry flow from failed step', async () => { + const failedResult = await flowExecutor.execute({ + action: failedHttpAction, executionState: context, constants: generateMockEngineConstants(), + }) + + const retryFromFailed = await flowExecutor.execute({ + action: successHttpAction, executionState: context, constants: generateMockEngineConstants({ + executionType: ExecutionType.RESUME, + }), + }) + expect(failedResult.verdict).toBe(ExecutionVerdict.FAILED) + expect(retryFromFailed.verdict).toBe(ExecutionVerdict.RUNNING) + }) +}) diff --git a/packages/engine/test/handler/flow-with-pause.test.ts b/packages/engine/test/handler/flow-with-pause.test.ts index 68ab3f1af..a50b7a7af 100644 --- a/packages/engine/test/handler/flow-with-pause.test.ts +++ b/packages/engine/test/handler/flow-with-pause.test.ts @@ -1,7 +1,7 @@ import { BranchOperator, ExecutionOutputStatus, ExecutionType, LoopStepOutput } from '@activepieces/shared' import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context' import { flowExecutor } from '../../src/lib/handler/flow-executor' -import { EXECUTE_CONSTANTS, buildActionWithOneCondition, buildCodeAction, buildPieceAction, buildSimpleLoopAction } from './test-helper' +import { buildActionWithOneCondition, buildCodeAction, buildPieceAction, buildSimpleLoopAction, generateMockEngineConstants } from './test-helper' import { StepExecutionPath } from '../../src/lib/handler/context/step-execution-path' @@ -35,7 +35,7 @@ describe('flow with pause', () => { const pauseResult = await flowExecutor.execute({ action: pauseFlowWithLoopAndBranch, executionState: FlowExecutorContext.empty(), - constants: EXECUTE_CONSTANTS, + constants: generateMockEngineConstants(), }) expect(pauseResult.verdict).toBe(ExecutionVerdict.PAUSED) expect(pauseResult.verdictResponse).toEqual({ @@ -50,13 +50,12 @@ describe('flow with pause', () => { const resumeResult = await flowExecutor.execute({ action: pauseFlowWithLoopAndBranch, executionState: pauseResult.setCurrentPath(StepExecutionPath.empty()), - constants: { - ...EXECUTE_CONSTANTS, + constants: generateMockEngineConstants({ resumePayload: { action: 'approve', }, executionType: ExecutionType.RESUME, - }, + }), }) expect(resumeResult.verdict).toBe(ExecutionVerdict.RUNNING) expect(Object.keys(resumeResult.steps)).toEqual(['loop']) @@ -68,7 +67,7 @@ describe('flow with pause', () => { const pauseResult = await flowExecutor.execute({ action: simplePauseFlow, executionState: FlowExecutorContext.empty(), - constants: EXECUTE_CONSTANTS, + constants: generateMockEngineConstants(), }) expect(pauseResult.verdict).toBe(ExecutionVerdict.PAUSED) expect(pauseResult.verdictResponse).toEqual({ @@ -83,13 +82,12 @@ describe('flow with pause', () => { const resumeResult = await flowExecutor.execute({ action: simplePauseFlow, executionState: pauseResult, - constants: { - ...EXECUTE_CONSTANTS, + constants: generateMockEngineConstants({ resumePayload: { action: 'approve', }, executionType: ExecutionType.RESUME, - }, + }), }) expect(resumeResult.verdict).toBe(ExecutionVerdict.RUNNING) expect(resumeResult.currentState).toEqual({ @@ -100,4 +98,4 @@ describe('flow with pause', () => { }) }) -}) \ No newline at end of file +}) diff --git a/packages/engine/test/handler/flow-with-response.test.ts b/packages/engine/test/handler/flow-with-response.test.ts index 50a3e91ce..98eaba8a7 100644 --- a/packages/engine/test/handler/flow-with-response.test.ts +++ b/packages/engine/test/handler/flow-with-response.test.ts @@ -1,6 +1,6 @@ import { ExecutionOutputStatus } from '@activepieces/shared' import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context' -import { EXECUTE_CONSTANTS, buildPieceAction } from './test-helper' +import { buildPieceAction, generateMockEngineConstants } from './test-helper' import { flowExecutor } from '../../src/lib/handler/flow-executor' describe('flow with response', () => { @@ -21,7 +21,7 @@ describe('flow with response', () => { pieceName: '@activepieces/piece-http', actionName: 'return_response', input: response, - }), executionState: FlowExecutorContext.empty(), constants: EXECUTE_CONSTANTS, + }), executionState: FlowExecutorContext.empty(), constants: generateMockEngineConstants(), }) expect(result.verdict).toBe(ExecutionVerdict.SUCCEEDED) expect(result.verdictResponse).toEqual({ @@ -31,4 +31,4 @@ describe('flow with response', () => { expect(result.steps.http.output).toEqual(response) }) -}) \ No newline at end of file +}) diff --git a/packages/engine/test/handler/test-helper.ts b/packages/engine/test/handler/test-helper.ts index 75dd12baf..039709937 100644 --- a/packages/engine/test/handler/test-helper.ts +++ b/packages/engine/test/handler/test-helper.ts @@ -1,25 +1,23 @@ import { Action, ActionType, BranchAction, BranchCondition, CodeAction, ExecutionType, LoopOnItemsAction, PackageType, PieceAction, PieceType } from '@activepieces/shared' -import path from 'path' -import { cwd } from 'process' import { VariableService } from '../../src/lib/services/variable-service' -import { EngineConstantData } from '../../src/lib/handler/context/engine-constants-data' +import { EngineConstants } from '../../src/lib/handler/context/engine-constants' -export const EXECUTE_CONSTANTS: EngineConstantData = { - flowId: 'flowId', - flowRunId: 'flowRunId', - serverUrl: 'http://localhost:3000', - apiUrl: 'http://localhost:3000', - projectId: 'projectId', - workerToken: 'workerToken', - variableService: new VariableService({ - projectId: 'projectId', - workerToken: 'workerToken', - }), - baseCodeDirectory: path.resolve(cwd(), 'packages', 'engine', 'test', 'resources', 'codes'), - executionType: ExecutionType.BEGIN, - piecesSource: 'FILE', - filesServiceType: 'local', - testSingleStepMode: false, +export const generateMockEngineConstants = (params?: Partial): EngineConstants => { + return new EngineConstants( + params?.flowId ?? 'flowId', + params?.flowRunId ?? 'flowRunId', + params?.serverUrl ?? 'http://127.0.0.1:3000', + params?.executionType ?? ExecutionType.BEGIN, + params?.workerToken ?? 'workerToken', + params?.projectId ?? 'projectId', + params?.variableService ?? new VariableService({ + projectId: 'projectId', + workerToken: 'workerToken', + }), + params?.testSingleStepMode ?? false, + params?.filesServiceType ?? 'local', + params?.resumePayload, + ) } export function buildSimpleLoopAction({ @@ -100,4 +98,4 @@ export function buildPieceAction({ name, input, pieceName, actionName, nextActio nextAction, valid: true, } -} \ No newline at end of file +} diff --git a/packages/pieces/airtable/package.json b/packages/pieces/airtable/package.json index 0f0fe90f2..db7d02d5c 100644 --- a/packages/pieces/airtable/package.json +++ b/packages/pieces/airtable/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-airtable", - "version": "0.3.7" + "version": "0.4.8" } \ No newline at end of file diff --git a/packages/pieces/airtable/src/index.ts b/packages/pieces/airtable/src/index.ts index bcf0f62a8..3847ca257 100644 --- a/packages/pieces/airtable/src/index.ts +++ b/packages/pieces/airtable/src/index.ts @@ -1,15 +1,20 @@ import { PieceAuth, createPiece } from '@activepieces/pieces-framework'; import { airtableCreateRecordAction } from './lib/actions/create-record'; import { airtableNewRecordTrigger } from './lib/trigger/new-record.trigger'; +import { airtableUpdatedRecordTrigger } from './lib/trigger/update-record.trigger'; import { airtableFindRecordAction } from './lib/actions/find-record'; import { airtableUpdateRecordAction } from './lib/actions/update-record'; import { airtableDeleteRecordAction } from './lib/actions/delete-record'; -import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; +import { + AuthenticationType, + HttpMethod, + httpClient, +} from '@activepieces/pieces-common'; export const airtableAuth = PieceAuth.SecretText({ - displayName: 'Personal Token', - required: true, - description: ` + displayName: 'Personal Access Token', + required: true, + description: ` To obtain your personal token, follow these steps: 1. Log in to your Airtable account. @@ -18,27 +23,27 @@ export const airtableAuth = PieceAuth.SecretText({ 4. Click on "+ Add a scope" and select "data.records.read", "data.records.write" and "schema.bases.read". 5. Click on "Create token" and copy the token. `, - validate: async (auth) => { - try{ - await httpClient.sendRequest({ - method: HttpMethod.GET, - url: "https://api.airtable.com/v0/meta/bases", - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: auth.auth - } - }) - return{ - valid: true, - } - }catch(e){ - return{ - valid: false, - error: 'Invalid API token' - } - } + validate: async (auth) => { + try { + await httpClient.sendRequest({ + method: HttpMethod.GET, + url: 'https://api.airtable.com/v0/meta/bases', + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: auth.auth, + }, + }); + return { + valid: true, + }; + } catch (e) { + return { + valid: false, + error: 'Invalid personal access token', + }; } -}) + }, +}); export const airtable = createPiece({ displayName: 'Airtable', @@ -52,5 +57,5 @@ export const airtable = createPiece({ airtableUpdateRecordAction, airtableDeleteRecordAction, ], - triggers: [airtableNewRecordTrigger], + triggers: [airtableNewRecordTrigger, airtableUpdatedRecordTrigger], }); diff --git a/packages/pieces/airtable/src/lib/actions/find-record.ts b/packages/pieces/airtable/src/lib/actions/find-record.ts index c479e5742..254bc26a9 100644 --- a/packages/pieces/airtable/src/lib/actions/find-record.ts +++ b/packages/pieces/airtable/src/lib/actions/find-record.ts @@ -11,7 +11,7 @@ export const airtableFindRecordAction = createAction({ props: { base: airtableCommon.base, tableId: airtableCommon.tableId, - fieldNames: airtableCommon.fieldNames, + searchField: airtableCommon.fieldNames, searchValue: Property.ShortText({ displayName: 'Search Value', required: true, @@ -23,7 +23,7 @@ export const airtableFindRecordAction = createAction({ const { base: baseId, tableId, - fieldNames, + searchField, searchValue, limitToView, } = context.propsValue; @@ -32,7 +32,7 @@ export const airtableFindRecordAction = createAction({ personalToken, baseId: baseId as string, tableId: tableId as string, - fieldNames: fieldNames as string[], + searchField: searchField as string, searchValue: searchValue as string, limitToView: limitToView as string, }); diff --git a/packages/pieces/airtable/src/lib/common/index.ts b/packages/pieces/airtable/src/lib/common/index.ts index bc63d2729..5ff87298b 100644 --- a/packages/pieces/airtable/src/lib/common/index.ts +++ b/packages/pieces/airtable/src/lib/common/index.ts @@ -236,27 +236,44 @@ export const airtableCommon = { }, }), - fieldNames: Property.DynamicProperties({ - displayName: 'Table', + fieldNames: Property.Dropdown({ + displayName: 'Search Field', required: true, refreshers: ['base', 'tableId'], - - props: async ({ auth, base, tableId }) => { - if (!auth) return {}; - if (!base) return {}; - if (!tableId) return {}; - - let fieldNames = {}; - + options: async ({ auth, base, tableId }) => { + if (!auth) { + return { + disabled: true, + options: [], + placeholder: 'Please connect your account', + }; + } + if (!base) { + return { + disabled: true, + options: [], + placeholder: 'Please select a base first', + }; + } + if (!tableId) { + return { + disabled: true, + options: [], + placeholder: 'Please select a table first', + }; + } const airtable: AirtableTable = await airtableCommon.fetchTable({ token: auth as unknown as string, baseId: base as unknown as string, tableId: tableId as unknown as string, }); - - fieldNames = airtable.fields.map((field: AirtableField) => field.name); - - return fieldNames; + return { + disabled: false, + options: airtable.fields.map((field: AirtableField) => ({ + label: field.name, + value: field.name, + })), + }; }, }), @@ -387,7 +404,7 @@ export const airtableCommon = { }, body: { fields, - typecast:true + typecast: true, }, }; @@ -494,4 +511,5 @@ interface Params { searchField?: string; fieldNames?: string[]; limitToView?: string; + sortField?: string; } diff --git a/packages/pieces/airtable/src/lib/trigger/update-record.trigger.ts b/packages/pieces/airtable/src/lib/trigger/update-record.trigger.ts new file mode 100644 index 000000000..87d5658d4 --- /dev/null +++ b/packages/pieces/airtable/src/lib/trigger/update-record.trigger.ts @@ -0,0 +1,126 @@ +import { + Property, + StaticPropsValue, + TriggerStrategy, + createTrigger, +} from '@activepieces/pieces-framework'; +import { airtableCommon } from '../common'; +import { + DedupeStrategy, + Polling, + pollingHelper, +} from '@activepieces/pieces-common'; +import { airtableAuth } from '../../'; +import { AirtableField, AirtableTable } from '../common/models'; +import Airtable from 'airtable'; +import dayjs from 'dayjs'; + +const props = { + base: airtableCommon.base, + tableId: airtableCommon.tableId, + sortFields: Property.Dropdown({ + displayName: 'Trigger field', + description: `**Last Modified Time** field will be used to watch new or updated records.Please create **Last Modified Time** field in your schema,if you don't have any timestamp field.`, + required: true, + refreshers: ['base', 'tableId'], + options: async ({ auth, base, tableId }) => { + if (!auth) { + return { + disabled: true, + options: [], + placeholder: 'Please connect your account', + }; + } + if (!base) { + return { + disabled: true, + options: [], + placeholder: 'Please select a base first', + }; + } + if (!tableId) { + return { + disabled: true, + options: [], + placeholder: 'Please select a table first', + }; + } + const airtable: AirtableTable = await airtableCommon.fetchTable({ + token: auth as unknown as string, + baseId: base as unknown as string, + tableId: tableId as unknown as string, + }); + + return { + disabled: false, + options: airtable.fields + .filter((field: AirtableField) => field.type == 'lastModifiedTime') + .map((field: AirtableField) => ({ + label: field.name, + value: field.name, + })), + }; + }, + }), +}; +const polling: Polling> = { + strategy: DedupeStrategy.TIMEBASED, + items: async ({ auth, propsValue, lastFetchEpochMS }) => { + Airtable.configure({ + apiKey: auth, + }); + const airtable = new Airtable(); + const currentValues = await airtable + .base(propsValue.base) + .table(propsValue.tableId!) + .select({ + sort: [{ direction: 'desc', field: propsValue.sortFields! }], + }) + .all(); + const records = currentValues.filter((record) => { + const modified_at = dayjs(record.fields[propsValue.sortFields] as string); + return modified_at.isAfter( + lastFetchEpochMS === 0 + ? dayjs().subtract(1, 'day').toISOString() + : dayjs(lastFetchEpochMS).toISOString() + ); + }); + return records.map((item) => { + return { + epochMilliSeconds: dayjs( + item.fields[propsValue.sortFields] as string + ).valueOf(), + data: item._rawJson, + }; + }); + }, +}; + +export const airtableUpdatedRecordTrigger = createTrigger({ + auth: airtableAuth, + name: 'updated_record', + displayName: 'New or Updated Record', + description: + 'Triggers when a record is created or updated in selected table.', + props, + sampleData: {}, + type: TriggerStrategy.POLLING, + async test(context) { + const { store, auth, propsValue } = context; + return await pollingHelper.test(polling, { store, auth, propsValue }); + }, + async onEnable(context) { + const { store, auth, propsValue } = context; + await pollingHelper.onEnable(polling, { store, auth, propsValue }); + }, + + async onDisable(context) { + const { store, auth, propsValue } = context; + await pollingHelper.onDisable(polling, { store, auth, propsValue }); + }, + + async run(context) { + const { store, auth, propsValue } = context; + return await pollingHelper.poll(polling, { store, auth, propsValue }); + }, +}); diff --git a/packages/pieces/alchemy/package.json b/packages/pieces/alchemy/package.json index 47b9482ab..614bd6acb 100644 --- a/packages/pieces/alchemy/package.json +++ b/packages/pieces/alchemy/package.json @@ -1,4 +1,4 @@ { "name": "@tookey-io/piece-alchemy", - "version": "0.0.2" + "version": "0.0.3" } diff --git a/packages/pieces/alchemy/src/index.ts b/packages/pieces/alchemy/src/index.ts index f325631bb..2d6aacd75 100644 --- a/packages/pieces/alchemy/src/index.ts +++ b/packages/pieces/alchemy/src/index.ts @@ -219,7 +219,6 @@ block { export const BlockEventTrigger = createTrigger({ displayName: 'On block event', auth: AlchemyHooksAuth, - requireAuth: true, name: 'block_event', description: 'Trigger on block event', props: { diff --git a/packages/pieces/apitable/package.json b/packages/pieces/apitable/package.json index 9718f062a..a15a2c5ca 100644 --- a/packages/pieces/apitable/package.json +++ b/packages/pieces/apitable/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-apitable", - "version": "0.0.5" + "version": "0.0.7" } diff --git a/packages/pieces/apitable/src/index.ts b/packages/pieces/apitable/src/index.ts index acfb365de..0f750ee7d 100644 --- a/packages/pieces/apitable/src/index.ts +++ b/packages/pieces/apitable/src/index.ts @@ -8,9 +8,9 @@ import { apiTableFindRecord } from "./lib/actions/find-record"; export const APITableAuth = PieceAuth.CustomAuth({ required: true, description: ` - To obtain your APITable token, follow these steps: + To obtain your AITable token, follow these steps: - 1. Log in to your ApiTable account. + 1. Log in to your AITable account. 2. Visit https://apitable.com/workbench 3. Click on your profile picture (Bottom left). 4. Click on "My Settings". @@ -28,13 +28,13 @@ export const APITableAuth = PieceAuth.CustomAuth({ displayName: 'Instance Url', description: 'The url of the APITable instance.', required: true, - defaultValue: 'https://api.apitable.com', + defaultValue: 'https://api.aitable.ai', }), } }) export const apitable = createPiece({ - displayName: "APITable", + displayName: "AITable", auth: APITableAuth, description: `Interactive spreadsheets with collaboration`, minimumSupportedRelease: '0.5.0', diff --git a/packages/pieces/apitable/src/lib/triggers/new-record.ts b/packages/pieces/apitable/src/lib/triggers/new-record.ts index 686899d4f..a3fa75194 100644 --- a/packages/pieces/apitable/src/lib/triggers/new-record.ts +++ b/packages/pieces/apitable/src/lib/triggers/new-record.ts @@ -48,7 +48,6 @@ const polling: Polling, { datasheet: s export const ApiTableNewRecord = createTrigger({ auth: APITableAuth, - requireAuth: true, name: 'new_record', displayName: 'New Record', description: 'Triggers when a new record is added to a datasheet.', diff --git a/packages/pieces/approval/package.json b/packages/pieces/approval/package.json index bc80dc67f..fb1fa0016 100644 --- a/packages/pieces/approval/package.json +++ b/packages/pieces/approval/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-approval", - "version": "0.1.0" + "version": "0.1.1" } diff --git a/packages/pieces/approval/src/lib/actions/wait-for-approval.ts b/packages/pieces/approval/src/lib/actions/wait-for-approval.ts index 393f4a00a..8310aaba4 100644 --- a/packages/pieces/approval/src/lib/actions/wait-for-approval.ts +++ b/packages/pieces/approval/src/lib/actions/wait-for-approval.ts @@ -16,7 +16,9 @@ export const waitForApprovalLink = createAction({ } }); - return {} + return { + approved: true + } } else { const payload = ctx.resumePayload as { action: string }; diff --git a/packages/pieces/beamer/.eslintrc.json b/packages/pieces/beamer/.eslintrc.json new file mode 100644 index 000000000..7cd4bf646 --- /dev/null +++ b/packages/pieces/beamer/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts", + "*.tsx", + "*.js", + "*.jsx" + ], + "rules": {} + }, + { + "files": [ + "*.ts", + "*.tsx" + ], + "rules": {} + }, + { + "files": [ + "*.js", + "*.jsx" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/packages/pieces/beamer/README.md b/packages/pieces/beamer/README.md new file mode 100644 index 000000000..2687d0f85 --- /dev/null +++ b/packages/pieces/beamer/README.md @@ -0,0 +1,7 @@ +# pieces-beamer + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-beamer` to build the library. diff --git a/packages/pieces/beamer/package.json b/packages/pieces/beamer/package.json new file mode 100644 index 000000000..cf20456d9 --- /dev/null +++ b/packages/pieces/beamer/package.json @@ -0,0 +1,4 @@ +{ + "name": "@activepieces/piece-beamer", + "version": "0.0.1" +} diff --git a/packages/pieces/beamer/project.json b/packages/pieces/beamer/project.json new file mode 100644 index 000000000..567382a83 --- /dev/null +++ b/packages/pieces/beamer/project.json @@ -0,0 +1,43 @@ +{ + "name": "pieces-beamer", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/beamer/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/beamer", + "tsConfig": "packages/pieces/beamer/tsconfig.lib.json", + "packageJson": "packages/pieces/beamer/package.json", + "main": "packages/pieces/beamer/src/index.ts", + "assets": [ + "packages/pieces/beamer/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-beamer {args.ver} {args.tag}", + "dependsOn": [ + "build" + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ], + "options": { + "lintFilePatterns": [ + "packages/pieces/beamer/**/*.ts" + ] + } + } + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/pieces/beamer/src/index.ts b/packages/pieces/beamer/src/index.ts new file mode 100644 index 000000000..2abf9b0d5 --- /dev/null +++ b/packages/pieces/beamer/src/index.ts @@ -0,0 +1,26 @@ +import { PieceAuth, createPiece } from '@activepieces/pieces-framework'; +import { createBeamerPost } from './lib/actions/create-posts'; +import { createNewFeatureRequest } from './lib/actions/create-feature-request'; +import { createComment } from './lib/actions/create-comment'; +import { newPost } from './lib/trigger/new-post'; +import { createVote } from './lib/actions/create-vote'; + +export const beamerAuth = PieceAuth.SecretText({ + displayName: 'API Key', + required: true, + description: 'API key acquired from your Beamer settings', +}); + +export const beamer = createPiece({ + displayName: 'beamer', + logoUrl: 'https://cdn.activepieces.com/pieces/beamer.png', + auth: beamerAuth, + authors: ['i-nithin'], + actions: [ + createBeamerPost, + createNewFeatureRequest, + createComment, + createVote, + ], + triggers: [newPost], +}); diff --git a/packages/pieces/beamer/src/lib/actions/create-comment.ts b/packages/pieces/beamer/src/lib/actions/create-comment.ts new file mode 100644 index 000000000..bcefad337 --- /dev/null +++ b/packages/pieces/beamer/src/lib/actions/create-comment.ts @@ -0,0 +1,57 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { beamerAuth } from '../../index'; +import { beamerCommon } from '../common'; +import { + HttpMethod, + HttpRequest, + httpClient, +} from '@activepieces/pieces-common'; + +export const createComment = createAction({ + auth: beamerAuth, + name: 'create_new_comment', + displayName: 'Create a new comment', + description: 'Create a new comment for a Feature request', + props: { + featureRequestId: Property.Number({ + displayName: 'ID', + description: 'ID of the feature request', + required: true, + }), + userId: Property.Number({ + displayName: 'User ID', + description: 'id of the user making the comment', + required: false, + }), + text: Property.LongText({ + displayName: 'Text', + description: 'Content of your comment', + required: true, + }), + userEmail: Property.ShortText({ + displayName: 'User Email', + description: 'Feature requested by..', + required: false, + }), + }, + async run(context) { + const apiKey = context.auth; + + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${beamerCommon.baseUrl}/requests/${context.propsValue.featureRequestId}/comments`, + headers: { + 'Beamer-Api-Key': `${apiKey}`, + 'Content-Type': 'application/json', + }, + body: { + text: context.propsValue.text, + userId: context.propsValue.userId, + userEmail: context.propsValue.userEmail, + visible: 'true', + }, + }; + const res = await httpClient.sendRequest(request); + return res.body; + }, +}); diff --git a/packages/pieces/beamer/src/lib/actions/create-feature-request.ts b/packages/pieces/beamer/src/lib/actions/create-feature-request.ts new file mode 100644 index 000000000..f5c9fcd90 --- /dev/null +++ b/packages/pieces/beamer/src/lib/actions/create-feature-request.ts @@ -0,0 +1,122 @@ +import { beamerAuth } from '../../index'; +import { + HttpMethod, + HttpRequest, + httpClient, +} from '@activepieces/pieces-common'; +import { Property, createAction } from '@activepieces/pieces-framework'; +import { beamerCommon } from '../common'; + +export const createNewFeatureRequest = createAction({ + auth: beamerAuth, + name: 'create_new_feature_request', + displayName: 'Create New Feature Request ', + description: 'Create New Feature Request', + props: { + title: Property.ShortText({ + displayName: 'Title', + description: 'Enter the Title of the post', + required: true, + }), + content: Property.LongText({ + displayName: 'Description', + description: 'Enter the description of the post', + required: true, + }), + visible: Property.StaticDropdown({ + displayName: 'Visibility', + description: 'Set visiblisty', + required: true, + options: { + options: [ + { + label: 'Public', + + value: true, + }, + { + label: 'Internal', + value: false, + }, + ], + }, + }), + category: Property.StaticDropdown({ + displayName: 'Category', + description: 'Set Category', + required: true, + options: { + options: [ + { + label: 'Bug', + + value: 'bug', + }, + { + label: 'Feature Request', + value: 'feature_request', + }, + { + label: 'Improvement', + value: 'improvement', + }, + ], + }, + }), + status: Property.StaticDropdown({ + displayName: 'Status', + description: 'Set status', + required: true, + options: { + options: [ + { + label: 'Under Review', + value: 'under_review', + }, + { + label: 'Planned', + value: 'planned', + }, + { + label: 'In Progess', + value: 'in_progress', + }, + { + label: 'Completed', + value: 'completed', + }, + ], + }, + }), + + requestedby: Property.ShortText({ + displayName: 'User Email', + description: 'Feature requested by..', + required: true, + }), + }, + + async run(context) { + const apiKey = context.auth; + + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${beamerCommon.baseUrl}/requests`, + headers: { + 'Beamer-Api-Key': `${apiKey}`, + 'Content-Type': 'application/json', + }, + body: { + title: [context.propsValue.title], + content: [context.propsValue.content], + visible: context.propsValue.visible, + category: context.propsValue.category, + status: context.propsValue.status, + userEmail: context.propsValue.requestedby, + }, + }; + const res = await httpClient.sendRequest(request); + + return res.body; + }, +}); diff --git a/packages/pieces/beamer/src/lib/actions/create-posts.ts b/packages/pieces/beamer/src/lib/actions/create-posts.ts new file mode 100644 index 000000000..0de0d39c9 --- /dev/null +++ b/packages/pieces/beamer/src/lib/actions/create-posts.ts @@ -0,0 +1,163 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { + HttpRequest, + HttpMethod, + httpClient, +} from '@activepieces/pieces-common'; +import { beamerAuth } from '../../index'; +import { beamerCommon } from '../common'; + +export const createBeamerPost = createAction({ + auth: beamerAuth, + name: 'create_beamer_post', + displayName: 'Create Beamer Post ', + description: 'Create a new post in Beamer', + props: { + title: Property.ShortText({ + displayName: 'Title', + description: 'Enter the Title of the post', + required: true, + defaultValue: 'Post name', + }), + description: Property.LongText({ + displayName: 'Description', + description: 'Enter the description of the post', + required: true, + defaultValue: 'desc...', + }), + category: Property.StaticDropdown({ + displayName: 'Category', + description: 'Set Category', + required: true, + options: { + options: [ + { + label: 'New', + value: 'new', + }, + { + label: 'Fix', + value: 'fix', + }, + { + label: 'Coming Soon ', + value: 'coming_soon', + }, + { + label: 'Announcement', + value: 'announcement', + }, + { + label: 'Improvement', + value: 'improvement', + }, + ], + }, + }), + showInWidget: Property.StaticDropdown({ + displayName: 'Show In Widget ', + description: 'Check to enable widget', + required: false, + options: { + options: [ + { + label: 'True', + value: true, + }, + { + label: 'False', + value: false, + }, + ], + }, + }), + showInStandalone: Property.StaticDropdown({ + displayName: 'Show In Standalone ', + description: 'Check to enable Standalone', + required: false, + options: { + options: [ + { + label: 'True', + value: true, + }, + { + label: 'False', + value: false, + }, + ], + }, + }), + enableFeedback: Property.Checkbox({ + displayName: 'Enable Feedback', + description: 'Check to enable feedback', + required: false, + defaultValue: false, + }), + enableReactions: Property.Checkbox({ + displayName: 'Enable Reactions', + description: 'Check to enable feedback', + required: false, + defaultValue: false, + }), + enableSocialShare: Property.Checkbox({ + displayName: 'Enable Social share', + description: 'Check to enable social share', + required: false, + defaultValue: false, + }), + autoOpen: Property.Checkbox({ + displayName: 'Enable Auto open', + description: 'Check to enable auto open', + required: false, + defaultValue: false, + }), + sendPushNotification: Property.Checkbox({ + displayName: 'Enable Push Notifications', + description: 'Check to enable Push Notifications', + required: false, + defaultValue: false, + }), + userEmail: Property.ShortText({ + displayName: 'User Email', + description: 'Enter user email', + required: true, + }), + }, + + async run(context) { + const apiKey = context.auth; + + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${beamerCommon.baseUrl}/posts`, + headers: { + 'Beamer-Api-Key': `${apiKey}`, + 'Content-Type': 'application/json', + }, + body: { + title: [context.propsValue.title], + content: [context.propsValue.description], + category: context.propsValue.category, + publish: true, + archive: false, + pinned: false, + showInWidget: context.propsValue.showInWidget, + showInStandalone: context.propsValue.showInStandalone, + enableFeedback: context.propsValue.enableFeedback, + enableReactions: context.propsValue.enableReactions, + enableSocialShare: context.propsValue.enableSocialShare, + autoOpen: context.propsValue.autoOpen, + sendPushNotification: context.propsValue.sendPushNotification, + userEmail: context.propsValue.userEmail, + fixedBoostedAnnouncement: true, + }, + }; + const res = await httpClient.sendRequest(request); + + return res.body; + }, +}); diff --git a/packages/pieces/beamer/src/lib/actions/create-vote.ts b/packages/pieces/beamer/src/lib/actions/create-vote.ts new file mode 100644 index 000000000..3b1b7c727 --- /dev/null +++ b/packages/pieces/beamer/src/lib/actions/create-vote.ts @@ -0,0 +1,47 @@ +import { + HttpMethod, + HttpRequest, +} from '@activepieces/pieces-common'; +import { beamerAuth } from '../../index'; +import { Property, createAction } from '@activepieces/pieces-framework'; +import { beamerCommon } from '../common'; + +export const createVote = createAction({ + auth: beamerAuth, + name: 'create_vote', + displayName: 'Create a new vote ', + description: 'Create a new vote on a feature request', + props: { + featureRequestId: Property.Number({ + displayName: 'Feature ID', + description: 'ID of the feature request to create a comment for', + required: true, + }), + userFirstname: Property.ShortText({ + displayName: 'User Firstname', + description: 'Feature requested by..', + required: false, + }), + userEmail: Property.ShortText({ + displayName: 'User Email', + description: 'Feature requested by..', + required: false, + }), + }, + async run(context) { + const apiKey = context.auth; + + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${beamerCommon.baseUrl}/requests/${context.propsValue.featureRequestId}/votes`, + headers: { + 'Beamer-Api-Key': `${apiKey}`, + 'Content-Type': 'application/json', + }, + body: { + userFirstname: context.propsValue.userFirstname, + email: context.propsValue.userEmail, + }, + }; + }, +}); diff --git a/packages/pieces/beamer/src/lib/common/index.ts b/packages/pieces/beamer/src/lib/common/index.ts new file mode 100644 index 000000000..0dfd4f2fb --- /dev/null +++ b/packages/pieces/beamer/src/lib/common/index.ts @@ -0,0 +1,4 @@ + +export const beamerCommon = { + baseUrl: 'https://api.getbeamer.com/v0', +}; diff --git a/packages/pieces/beamer/src/lib/trigger/new-post.ts b/packages/pieces/beamer/src/lib/trigger/new-post.ts new file mode 100644 index 000000000..963b6b5e7 --- /dev/null +++ b/packages/pieces/beamer/src/lib/trigger/new-post.ts @@ -0,0 +1,36 @@ +import { + Property, + TriggerStrategy, + createTrigger, +} from '@activepieces/pieces-framework'; + +const markdown = ` +- Go to the "Integration" section. +- Find and click on the "Webhook" plugin to activate it. +- Add a webhook to that form. +- In the webhook settings, paste this URL: + \`{{webhookUrl}}\` +`; + +export const newPost = createTrigger({ + name: 'new_post_on_beamer', + displayName: 'New Beamer Post', + description: 'Triggers when new post is found in your beamer account', + props: { + md: Property.MarkDown({ + value: markdown, + }), + }, + type: TriggerStrategy.WEBHOOK, + sampleData: {}, + + async onEnable(context) { + // IGNORED + }, + async onDisable(context) { + // IGNORED + }, + async run(context) { + return [context.payload.body]; + }, +}); diff --git a/packages/pieces/beamer/tsconfig.json b/packages/pieces/beamer/tsconfig.json new file mode 100644 index 000000000..f2400abed --- /dev/null +++ b/packages/pieces/beamer/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/pieces/beamer/tsconfig.lib.json b/packages/pieces/beamer/tsconfig.lib.json new file mode 100644 index 000000000..e583571ea --- /dev/null +++ b/packages/pieces/beamer/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/packages/pieces/brilliant-directories/package.json b/packages/pieces/brilliant-directories/package.json index db415ce2f..26ec3a3e6 100644 --- a/packages/pieces/brilliant-directories/package.json +++ b/packages/pieces/brilliant-directories/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-brilliant-directories", - "version": "0.0.1", + "version": "0.0.2", "dependencies": { "@activepieces/pieces-framework": "latest", "@activepieces/pieces-common": "latest", diff --git a/packages/pieces/clickup/package.json b/packages/pieces/clickup/package.json index 6fd5c0745..47c84039c 100644 --- a/packages/pieces/clickup/package.json +++ b/packages/pieces/clickup/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-clickup", - "version": "0.4.1" + "version": "0.4.3" } diff --git a/packages/pieces/clickup/src/index.ts b/packages/pieces/clickup/src/index.ts index 98f746518..accf919a1 100644 --- a/packages/pieces/clickup/src/index.ts +++ b/packages/pieces/clickup/src/index.ts @@ -10,6 +10,8 @@ import { createClickupSubtask } from './lib/actions/tasks/create-subtask'; import { clickupTriggers as triggers } from './lib/triggers'; import { createClickupTaskFromTemplate } from './lib/actions/tasks/create-task-from-template'; import { updateClickupTask } from './lib/actions/tasks/update-task'; +import { getClickupTasks } from './lib/actions/tasks/get-tasks'; +import { getClickupTask } from './lib/actions/tasks/get-task'; export const clickupAuth = PieceAuth.OAuth2({ description: '', @@ -29,6 +31,8 @@ export const clickup = createPiece({ createClickupTaskFromTemplate, createClickupFolderlessList, getClickupList, + getClickupTask, + getClickupTasks, getClickupSpace, getClickupSpaces, getClickupTaskComments, diff --git a/packages/pieces/clickup/src/lib/actions/tasks/get-task.ts b/packages/pieces/clickup/src/lib/actions/tasks/get-task.ts new file mode 100644 index 000000000..1cf5c5287 --- /dev/null +++ b/packages/pieces/clickup/src/lib/actions/tasks/get-task.ts @@ -0,0 +1,27 @@ +import { Property, createAction } from "@activepieces/pieces-framework"; +import { HttpMethod, getAccessTokenOrThrow } from "@activepieces/pieces-common"; +import { callClickUpApi } from "../../common"; +import { clickupAuth } from "../../../"; + + +export const getClickupTask = createAction({ + auth: clickupAuth, + name: 'get_list_task', + description: 'Gets a task in a ClickUp list', + displayName: 'Get Task', + props: { + task_id: Property.ShortText({ + description: 'The id of the tas to get', + displayName: 'Task ID', + required: true, + }), + }, + async run(configValue) { + const { task_id } = configValue.propsValue; + const response = await callClickUpApi(HttpMethod.GET, + `task/${task_id}`, getAccessTokenOrThrow(configValue.auth), { + }); + + return response.body; + }, +}); diff --git a/packages/pieces/clickup/src/lib/actions/tasks/get-tasks.ts b/packages/pieces/clickup/src/lib/actions/tasks/get-tasks.ts new file mode 100644 index 000000000..eabbe5d0c --- /dev/null +++ b/packages/pieces/clickup/src/lib/actions/tasks/get-tasks.ts @@ -0,0 +1,22 @@ +import { createAction } from "@activepieces/pieces-framework"; +import { getAccessTokenOrThrow } from "@activepieces/pieces-common"; +import { clickupCommon, listTasks } from "../../common"; +import { clickupAuth } from "../../../"; + +export const getClickupTasks = createAction({ + auth: clickupAuth, + name: 'get_list_tasks', + description: 'Gets a list of task from a list', + displayName: 'Get Tasks', + props: { + workspace_id: clickupCommon.workspace_id(true), + space_id: clickupCommon.space_id(true), + list_id: clickupCommon.list_id(true), + }, + async run(configValue) { + const { list_id } = configValue.propsValue; + const response = await listTasks(getAccessTokenOrThrow(configValue.auth), list_id as unknown as string) + + return response.tasks; + }, +}); diff --git a/packages/pieces/clickup/src/lib/common/index.ts b/packages/pieces/clickup/src/lib/common/index.ts index 6db5c94fe..5cd7556e6 100644 --- a/packages/pieces/clickup/src/lib/common/index.ts +++ b/packages/pieces/clickup/src/lib/common/index.ts @@ -77,7 +77,7 @@ export const clickupCommon = { description: 'The ID of the ClickUp space to create the task in', displayName: 'List', required, - refreshers: ['space_id'], + refreshers: ['space_id', 'workspace_id'], defaultValue: null, options: async ({ auth, space_id }) => { if (!auth || !space_id) { @@ -121,7 +121,7 @@ export const clickupCommon = { displayName: 'Task Id', required, defaultValue: null, - refreshers: ['space_id', 'list_id'], + refreshers: ['space_id', 'list_id', 'workspace_id'], options: async ({ auth, space_id, list_id }) => { if (!auth || !list_id || !space_id) { return { @@ -413,7 +413,7 @@ async function listFolderlessList(accessToken: string, spaceId: string) { ).body; } -async function listTasks(accessToken: string, listId: string) { +export async function listTasks(accessToken: string, listId: string) { return ( await callClickUpApi<{ tasks: { diff --git a/packages/pieces/clickup/src/lib/triggers/index.ts b/packages/pieces/clickup/src/lib/triggers/index.ts index 65b9cd1da..323a7566c 100644 --- a/packages/pieces/clickup/src/lib/triggers/index.ts +++ b/packages/pieces/clickup/src/lib/triggers/index.ts @@ -1,7 +1,8 @@ import { ClickupEventType } from '../common/models'; import { clickupRegisterTrigger } from './register-trigger'; +import { triggerTaskTagUpdated } from './task-tag-updated'; -const sampleTask = { +export const sampleTask = { id: 'string', custom_id: 'string', name: 'string', @@ -64,7 +65,7 @@ const sampleTask = { url: 'string', }; -export const clickupTriggers = [ +export const triggers = [ { name: 'task_created', eventType: ClickupEventType.TASK_CREATED, @@ -232,3 +233,5 @@ export const clickupTriggers = [ }, }, ].map((props) => clickupRegisterTrigger(props)); + +export const clickupTriggers = [...triggers, triggerTaskTagUpdated]; \ No newline at end of file diff --git a/packages/pieces/clickup/src/lib/triggers/task-tag-updated.ts b/packages/pieces/clickup/src/lib/triggers/task-tag-updated.ts new file mode 100644 index 000000000..dc0630873 --- /dev/null +++ b/packages/pieces/clickup/src/lib/triggers/task-tag-updated.ts @@ -0,0 +1,128 @@ +import { + TriggerStrategy, createTrigger +} from '@activepieces/pieces-framework'; +import { + httpClient, + HttpRequest, + HttpMethod, + AuthenticationType, +} from "@activepieces/pieces-common"; +import { callClickupGetTask, clickupCommon } from '../common'; +import { ClickupEventType, ClickupWebhookPayload } from '../common/models'; +import { clickupAuth } from "../../"; + +export const triggerTaskTagUpdated = createTrigger({ + auth: clickupAuth, + name: 'task_tag_updated', + displayName: 'Task Tag Updated', + description: 'Triggered when a tag is added or removed or renamed on a task.', + sampleData: { + "event": "taskTagUpdated", + "history_items": [ + { + "id": "2800797048554170804", + "type": 1, + "date": "1642736652800", + "field": "tag", + "parent_id": "162641062", + "data": {}, + "source": null, + "user": { + "id": 183, + "username": "John", + "email": "john@company.com", + "color": "#7b68ee", + "initials": "J", + "profilePicture": null + }, + "before": null, + "after": [ + { + "name": "def", + "tag_fg": "#FF4081", + "tag_bg": "#FF4081", + "creator": 2770032 + } + ] + } + ], + "task_id": "1vj38vv", + "webhook_id": "7fa3ec74-69a8-4530-a251-8a13730bd204" + }, + props: { + workspace_id: clickupCommon.workspace_id(true), + space_id: clickupCommon.space_id(true), + list_id: clickupCommon.list_id(false), + task_id: clickupCommon.task_id(false), + }, + type: TriggerStrategy.WEBHOOK, + async onEnable(context) { + const { workspace_id } = context.propsValue + + const request: HttpRequest = { + method: HttpMethod.POST, + url: `https://api.clickup.com/api/v2/team/${workspace_id}/webhook`, + body: { + endpoint: context.webhookUrl, + events: [ClickupEventType.TASK_TAG_UPDATED] + }, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: context.auth.access_token + }, + queryParams: {}, + } + + const response = await httpClient.sendRequest(request); + console.debug(`clickup.${ClickupEventType.TASK_TAG_UPDATED}.onEnable`, response) + + await context.store.put(`clickup_task_tag_updated_trigger`, response.body); + }, + async onDisable(context) { + const webhook = await context.store.get(`clickup_task_tag_updated_trigger`); + if (webhook != null) { + const request: HttpRequest = { + method: HttpMethod.DELETE, + url: `https://api.clickup.com/api/v2/webhook/${webhook.id}`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: context.auth['access_token'], + }, + }; + const response = await httpClient.sendRequest(request); + console.debug(`clickup.${ClickupEventType.TASK_TAG_UPDATED}.onDisable`, response) + } + }, + async run(context) { + const payload = context.payload.body as ClickupWebhookPayload + + return [{ + ...payload, + task: await callClickupGetTask( + context.auth['access_token'], + payload.task_id + ) + }]; + } +}); + +interface WebhookInformation { + id: string + webhook: { + id: string + userid: number + team_id: number + endpoint: string + client_id: string + events: ClickupEventType[] + task_id: string + list_id: string + folder_id: string + space_id: string + health: { + status: string + fail_count: number + }, + secret: string + } +} diff --git a/packages/pieces/epn/package.json b/packages/pieces/epn/package.json index 09ea77eef..6b88a8819 100644 --- a/packages/pieces/epn/package.json +++ b/packages/pieces/epn/package.json @@ -1,4 +1,4 @@ { "name": "@tookey-io/piece-epn", - "version": "0.0.4" + "version": "0.0.5" } diff --git a/packages/pieces/epn/src/lib/trigger/balanceChange.ts b/packages/pieces/epn/src/lib/trigger/balanceChange.ts index 9c826620c..52ad8b9b5 100644 --- a/packages/pieces/epn/src/lib/trigger/balanceChange.ts +++ b/packages/pieces/epn/src/lib/trigger/balanceChange.ts @@ -32,7 +32,6 @@ const polling: Polling, {}> = { export const BalanceChange = createTrigger({ auth: EPNAuth, - requireAuth: true, name: 'balance_change', displayName: 'Balance change', description: 'Triggers when account balance is changed', diff --git a/packages/pieces/epn/src/lib/trigger/balanceLowerThan.ts b/packages/pieces/epn/src/lib/trigger/balanceLowerThan.ts index 9d0651eb9..34df4ec8a 100644 --- a/packages/pieces/epn/src/lib/trigger/balanceLowerThan.ts +++ b/packages/pieces/epn/src/lib/trigger/balanceLowerThan.ts @@ -33,7 +33,6 @@ const polling: Polling, {}> = { export const BalanceLowerThan = createTrigger({ auth: EPNAuth, - requireAuth: true, name: 'balance_lower_than', displayName: 'Balance lower than', description: 'Triggers when account balance is lower than specified value', diff --git a/packages/pieces/epn/src/lib/trigger/cardIssued.ts b/packages/pieces/epn/src/lib/trigger/cardIssued.ts index 140d22791..c88397e63 100644 --- a/packages/pieces/epn/src/lib/trigger/cardIssued.ts +++ b/packages/pieces/epn/src/lib/trigger/cardIssued.ts @@ -32,7 +32,6 @@ const polling: Polling, {}> = { export const CardIssued = createTrigger({ auth: EPNAuth, - requireAuth: true, name: 'card_issued', displayName: 'Card Issued', description: 'Triggers when a card is issued', diff --git a/packages/pieces/flowise/.eslintrc.json b/packages/pieces/flowise/.eslintrc.json new file mode 100644 index 000000000..7cd4bf646 --- /dev/null +++ b/packages/pieces/flowise/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts", + "*.tsx", + "*.js", + "*.jsx" + ], + "rules": {} + }, + { + "files": [ + "*.ts", + "*.tsx" + ], + "rules": {} + }, + { + "files": [ + "*.js", + "*.jsx" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/packages/pieces/flowise/README.md b/packages/pieces/flowise/README.md new file mode 100644 index 000000000..31c955ed0 --- /dev/null +++ b/packages/pieces/flowise/README.md @@ -0,0 +1,7 @@ +# pieces-flowise + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-flowise` to build the library. diff --git a/packages/pieces/flowise/package-lock.json b/packages/pieces/flowise/package-lock.json new file mode 100644 index 000000000..617690770 --- /dev/null +++ b/packages/pieces/flowise/package-lock.json @@ -0,0 +1,29 @@ +{ + "name": "@activepieces/piece-flowise", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@activepieces/piece-flowise", + "version": "0.0.1", + "devDependencies": { + "@types/is-url": "^1.2.32" + } + }, + "node_modules/@types/is-url": { + "version": "1.2.32", + "resolved": "https://registry.npmjs.org/@types/is-url/-/is-url-1.2.32.tgz", + "integrity": "sha512-46VLdbWI8Sc+hPexQ6NLNR2YpoDyDZIpASHkJQ2Yr+Kf9Giw6LdCTkwOdsnHKPQeh7xTjTmSnxbE8qpxYuCiHA==", + "dev": true + } + }, + "dependencies": { + "@types/is-url": { + "version": "1.2.32", + "resolved": "https://registry.npmjs.org/@types/is-url/-/is-url-1.2.32.tgz", + "integrity": "sha512-46VLdbWI8Sc+hPexQ6NLNR2YpoDyDZIpASHkJQ2Yr+Kf9Giw6LdCTkwOdsnHKPQeh7xTjTmSnxbE8qpxYuCiHA==", + "dev": true + } + } +} diff --git a/packages/pieces/flowise/package.json b/packages/pieces/flowise/package.json new file mode 100644 index 000000000..040fe4020 --- /dev/null +++ b/packages/pieces/flowise/package.json @@ -0,0 +1,7 @@ +{ + "name": "@activepieces/piece-flowise", + "version": "0.0.1", + "devDependencies": { + "@types/is-url": "^1.2.32" + } +} diff --git a/packages/pieces/flowise/project.json b/packages/pieces/flowise/project.json new file mode 100644 index 000000000..8aee75349 --- /dev/null +++ b/packages/pieces/flowise/project.json @@ -0,0 +1,43 @@ +{ + "name": "pieces-flowise", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/flowise/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/flowise", + "tsConfig": "packages/pieces/flowise/tsconfig.lib.json", + "packageJson": "packages/pieces/flowise/package.json", + "main": "packages/pieces/flowise/src/index.ts", + "assets": [ + "packages/pieces/flowise/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-flowise {args.ver} {args.tag}", + "dependsOn": [ + "build" + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ], + "options": { + "lintFilePatterns": [ + "packages/pieces/flowise/**/*.ts" + ] + } + } + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/pieces/flowise/src/index.ts b/packages/pieces/flowise/src/index.ts new file mode 100644 index 000000000..dbaeb2065 --- /dev/null +++ b/packages/pieces/flowise/src/index.ts @@ -0,0 +1,82 @@ + +import {createAction, createPiece, PieceAuth, Property } from "@activepieces/pieces-framework"; + +const flowiseAuth = PieceAuth.CustomAuth({ + description: 'Enter your Flowise URL and API Key', + props: { + base_url: Property.ShortText({ + displayName: 'Base URL', + description: 'Enter the base URL', + required: true, + }), + access_token: PieceAuth.SecretText({ + displayName: 'API Key', + description: 'Enter the API Key', + required: true + }) + }, + required: true +}) + + +// /api/v1/prediction/{your-chatflowid} +export const flowisePredict = createAction({ + name: "make_prediction", + displayName: "Make Prediction", + description: "Run Flowise Predict", + auth: flowiseAuth, + props: { + chatflow_id: Property.ShortText({ + displayName: "Chatflow ID", + description: "Enter the Chatflow ID", + required: true, + }), + input: Property.ShortText({ + displayName: "Input/Question", + description: "Enter the Input/Question", + required: true, + }), + history: Property.Json({ + displayName: "History", + description: "Enter the History", + required: false, + }), + overrideConfig: Property.Json({ + displayName: "Override Config", + description: "Enter the Override Config", + required: false, + }), + }, + async run (ctx) { + const { base_url, access_token } = ctx.auth; + const chatflow_id = ctx.propsValue['chatflow_id'] + const input = ctx.propsValue['input'] + const url = `${base_url}/api/v1/prediction/${chatflow_id}`; + const headers = { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${access_token}` + } + const body = { + "question": input, + "history": ctx.propsValue['history'], + "overrideConfig": ctx.propsValue['overrideConfig'] + } + const response = await fetch(url, { + method: 'POST', + headers, + body: JSON.stringify(body) + }) + const data = await response.json(); + return data; + } +}) + +export const flowise = createPiece({ + displayName: "Flowise", + logoUrl: "https://cdn.activepieces.com/pieces/flowise.png", + auth: flowiseAuth, + minimumSupportedRelease: '0.9.0', + authors: [], + actions: [flowisePredict], + triggers: [], +}); diff --git a/packages/pieces/flowise/tsconfig.json b/packages/pieces/flowise/tsconfig.json new file mode 100644 index 000000000..f2400abed --- /dev/null +++ b/packages/pieces/flowise/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/pieces/flowise/tsconfig.lib.json b/packages/pieces/flowise/tsconfig.lib.json new file mode 100644 index 000000000..e583571ea --- /dev/null +++ b/packages/pieces/flowise/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/packages/pieces/flowlu/.eslintrc.json b/packages/pieces/flowlu/.eslintrc.json new file mode 100644 index 000000000..7cd4bf646 --- /dev/null +++ b/packages/pieces/flowlu/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts", + "*.tsx", + "*.js", + "*.jsx" + ], + "rules": {} + }, + { + "files": [ + "*.ts", + "*.tsx" + ], + "rules": {} + }, + { + "files": [ + "*.js", + "*.jsx" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/packages/pieces/flowlu/README.md b/packages/pieces/flowlu/README.md new file mode 100644 index 000000000..0b54a447e --- /dev/null +++ b/packages/pieces/flowlu/README.md @@ -0,0 +1,7 @@ +# pieces-flowlu + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-flowlu` to build the library. diff --git a/packages/pieces/flowlu/package.json b/packages/pieces/flowlu/package.json new file mode 100644 index 000000000..64a57b3fd --- /dev/null +++ b/packages/pieces/flowlu/package.json @@ -0,0 +1,4 @@ +{ + "name": "@activepieces/piece-flowlu", + "version": "0.0.1" +} diff --git a/packages/pieces/flowlu/project.json b/packages/pieces/flowlu/project.json new file mode 100644 index 000000000..23d4f9eb4 --- /dev/null +++ b/packages/pieces/flowlu/project.json @@ -0,0 +1,43 @@ +{ + "name": "pieces-flowlu", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/flowlu/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/flowlu", + "tsConfig": "packages/pieces/flowlu/tsconfig.lib.json", + "packageJson": "packages/pieces/flowlu/package.json", + "main": "packages/pieces/flowlu/src/index.ts", + "assets": [ + "packages/pieces/flowlu/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-flowlu {args.ver} {args.tag}", + "dependsOn": [ + "build" + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ], + "options": { + "lintFilePatterns": [ + "packages/pieces/flowlu/**/*.ts" + ] + } + } + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/pieces/flowlu/src/index.ts b/packages/pieces/flowlu/src/index.ts new file mode 100644 index 000000000..200ac0242 --- /dev/null +++ b/packages/pieces/flowlu/src/index.ts @@ -0,0 +1,59 @@ +import { + createPiece, + PieceAuth, + Property, +} from '@activepieces/pieces-framework'; +import { createContactAction } from './lib/actions/accounts/create-contact'; +import { createOrganizationAction } from './lib/actions/accounts/create-organization'; +import { deleteContactAction } from './lib/actions/accounts/delete-contact'; +import { updateContactAction } from './lib/actions/accounts/update-contact'; +import { createOpportunityAction } from './lib/actions/opportunities/create-opportunity'; +import { deleteOpportunityAction } from './lib/actions/opportunities/delete-opportunity'; +import { updateOpportunityAction } from './lib/actions/opportunities/update-opportunity'; +import { createTaskAction } from './lib/actions/tasks/create-task'; +import { deleteTaskAction } from './lib/actions/tasks/delete-task'; +import { getTaskAction } from './lib/actions/tasks/get-task'; +import { updateTaskAction } from './lib/actions/tasks/update-task'; + +export const flowluAuth = PieceAuth.CustomAuth({ + required: true, + description: ` + 1. Log in to your flowlu account. + 2. Click on your profile-pic(top-right) and navigate to **Portal Settings->API Settings**. + 3. Create new API key with any name and appropriate scope. + 4. Copy API Key to your clipboard and paste it in **API Key** field + 5. In the Domain field, enter your company from your account URL address. For example, if your account URL address is https://example.flowlu.com, then your domain is **example**. + `, + props: { + domain: Property.ShortText({ + displayName: 'Domain', + required: true, + }), + apiKey: PieceAuth.SecretText({ + displayName: 'API Key', + required: true, + }), + }, +}); + +export const flowlu = createPiece({ + displayName: 'Flowlu', + auth: flowluAuth, + minimumSupportedRelease: '0.9.0', + logoUrl: 'https://cdn.activepieces.com/pieces/flowlu.png', + authors: ['kishanprmr'], + actions: [ + createContactAction, + updateContactAction, + deleteContactAction, + createOrganizationAction, + createOpportunityAction, + updateOpportunityAction, + deleteOpportunityAction, + createTaskAction, + updateTaskAction, + getTaskAction, + deleteTaskAction, + ], + triggers: [], +}); diff --git a/packages/pieces/flowlu/src/lib/actions/accounts/create-contact.ts b/packages/pieces/flowlu/src/lib/actions/accounts/create-contact.ts new file mode 100644 index 000000000..64c24e38d --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/accounts/create-contact.ts @@ -0,0 +1,37 @@ +import { + PiecePropValueSchema, + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../..'; +import { flowluCommon, makeClient } from '../../common'; +import { flowluProps } from '../../common/props'; + +export const createContactAction = createAction({ + auth: flowluAuth, + name: 'flowlu_create_contact', + displayName: 'Create CRM Account(Contact)', + description: 'Creates a new contact in CRM.', + props: { + honorific_title_id: flowluCommon.honorific_title_id(false), + first_name: Property.ShortText({ + displayName: 'First Name', + required: true, + }), + middle_name: Property.ShortText({ + displayName: 'Middle Name', + required: false, + }), + last_name: Property.ShortText({ + displayName: 'Last Name', + required: false, + }), + ...flowluProps.account, + }, + async run(context) { + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.createAccount({ type: 2, ...context.propsValue }); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/accounts/create-organization.ts b/packages/pieces/flowlu/src/lib/actions/accounts/create-organization.ts new file mode 100644 index 000000000..461f6ef4a --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/accounts/create-organization.ts @@ -0,0 +1,32 @@ +import { + PiecePropValueSchema, + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../..'; +import { makeClient } from '../../common'; +import { flowluProps } from '../../common/props'; + +export const createOrganizationAction = createAction({ + auth: flowluAuth, + name: 'flowlu_create_organization', + displayName: 'Create CRM Account(Organization)', + description: 'Creates a new organization in CRM.', + props: { + name: Property.ShortText({ + displayName: 'Organization Name', + required: true, + }), + name_legal_full: Property.ShortText({ + displayName: 'Full legal name for Organization', + required: false, + }), + ...flowluProps.account, + }, + async run(context) { + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.createAccount({ type: 1, ...context.propsValue }); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/accounts/delete-contact.ts b/packages/pieces/flowlu/src/lib/actions/accounts/delete-contact.ts new file mode 100644 index 000000000..233e3b02b --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/accounts/delete-contact.ts @@ -0,0 +1,28 @@ +import { + createAction, + PiecePropValueSchema, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../..'; +import { flowluCommon, makeClient } from '../../common'; +import { FlowluEntity, FlowluModule } from '../../common/constants'; + +export const deleteContactAction = createAction({ + auth: flowluAuth, + name: 'flowlu_delete_contact', + displayName: 'Delete CRM Account(Contact)', + description: 'Deletes an existing contact in CRM.', + props: { + id: flowluCommon.contact_id(true), + }, + async run(context) { + const id = context.propsValue.id!; + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.deleteAction( + FlowluModule.CRM, + FlowluEntity.ACCOUNT, + id + ); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/accounts/update-contact.ts b/packages/pieces/flowlu/src/lib/actions/accounts/update-contact.ts new file mode 100644 index 000000000..eb4294f20 --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/accounts/update-contact.ts @@ -0,0 +1,39 @@ +import { + PiecePropValueSchema, + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../..'; +import { flowluCommon, makeClient } from '../../common'; +import { flowluProps } from '../../common/props'; + +export const updateContactAction = createAction({ + auth: flowluAuth, + name: 'flowlu_update_contact', + displayName: 'Update CRM Account(Contact)', + description: 'Updates an existing contact in CRM.', + props: { + id: flowluCommon.contact_id(true), + honorific_title_id: flowluCommon.honorific_title_id(false), + first_name: Property.ShortText({ + displayName: 'First Name', + required: true, + }), + middle_name: Property.ShortText({ + displayName: 'Middle Name', + required: false, + }), + last_name: Property.ShortText({ + displayName: 'Last Name', + required: false, + }), + ...flowluProps.account, + }, + async run(context) { + const id = context.propsValue.id!; + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.updateContact(id, { type: 2, ...context.propsValue }); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/opportunities/create-opportunity.ts b/packages/pieces/flowlu/src/lib/actions/opportunities/create-opportunity.ts new file mode 100644 index 000000000..55746f8d5 --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/opportunities/create-opportunity.ts @@ -0,0 +1,23 @@ +import { + createAction, + PiecePropValueSchema, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../../'; +import { makeClient } from '../../common'; +import { flowluProps } from '../../common/props'; + +export const createOpportunityAction = createAction({ + auth: flowluAuth, + name: 'flowlu_create_opportunity', + displayName: 'Create Opportunity', + description: 'Creates a new opportunity.', + props: { + ...flowluProps.opportunity, + }, + async run(context) { + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.createOpportunity(context.propsValue); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/opportunities/delete-opportunity.ts b/packages/pieces/flowlu/src/lib/actions/opportunities/delete-opportunity.ts new file mode 100644 index 000000000..742e0090e --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/opportunities/delete-opportunity.ts @@ -0,0 +1,28 @@ +import { + createAction, + PiecePropValueSchema, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../../'; +import { flowluCommon, makeClient } from '../../common'; +import { FlowluEntity, FlowluModule } from '../../common/constants'; + +export const deleteOpportunityAction = createAction({ + auth: flowluAuth, + name: 'flowlu_delete_opportunity', + displayName: 'Delete Opportunity', + description: 'Deletes an existing opportunity.', + props: { + id: flowluCommon.opportunity_id(true), + }, + async run(context) { + const id = context.propsValue.id!; + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.deleteAction( + FlowluModule.CRM, + FlowluEntity.OPPORTUNITY, + id + ); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/opportunities/update-opportunity.ts b/packages/pieces/flowlu/src/lib/actions/opportunities/update-opportunity.ts new file mode 100644 index 000000000..af3fc076f --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/opportunities/update-opportunity.ts @@ -0,0 +1,25 @@ +import { + createAction, + PiecePropValueSchema, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../../'; +import { flowluCommon, makeClient } from '../../common'; +import { flowluProps } from '../../common/props'; + +export const updateOpportunityAction = createAction({ + auth: flowluAuth, + name: 'flowlu_update_opportunity', + displayName: 'Update Opportunity', + description: 'Updates an existing opportunity.', + props: { + id: flowluCommon.opportunity_id(true), + ...flowluProps.opportunity, + }, + async run(context) { + const id = context.propsValue.id!; + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.updateOpportunity(id, context.propsValue); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/tasks/create-task.ts b/packages/pieces/flowlu/src/lib/actions/tasks/create-task.ts new file mode 100644 index 000000000..6ccb38d08 --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/tasks/create-task.ts @@ -0,0 +1,43 @@ +import { + PiecePropValueSchema, + createAction, +} from '@activepieces/pieces-framework'; +import dayjs from 'dayjs'; +import { flowluAuth } from '../../../'; +import { makeClient } from '../../common'; +import { flowluProps } from '../../common/props'; + +export const createTaskAction = createAction({ + auth: flowluAuth, + name: 'flowlu_create_task', + displayName: 'Create Task', + description: 'Creates a new task.', + props: { + ...flowluProps.task, + }, + async run(context) { + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.createTask({ + name: context.propsValue.name, + description: context.propsValue.description, + priority: context.propsValue.priority, + plan_start_date: context.propsValue.plan_start_date + ? dayjs(context.propsValue.plan_start_date).format( + 'YYYY-MM-DD HH:mm:ss' + ) + : undefined, + deadline: context.propsValue.deadline + ? dayjs(context.propsValue.deadline).format('YYYY-MM-DD HH:mm:ss') + : undefined, + deadline_allowchange: context.propsValue.deadline_allowchange ? 1 : 0, + task_checkbyowner: context.propsValue.task_checkbyowner ? 1 : 0, + responsible_id: context.propsValue.responsible_id, + owner_id: context.propsValue.owner_id, + type: context.propsValue.type, + workflow_id: context.propsValue.workflow_id, + workflow_stage_id: context.propsValue.workflow_stage_id, + }); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/tasks/delete-task.ts b/packages/pieces/flowlu/src/lib/actions/tasks/delete-task.ts new file mode 100644 index 000000000..02ea0f268 --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/tasks/delete-task.ts @@ -0,0 +1,28 @@ +import { + createAction, + PiecePropValueSchema, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../..'; +import { flowluCommon, makeClient } from '../../common'; +import { FlowluEntity, FlowluModule } from '../../common/constants'; + +export const deleteTaskAction = createAction({ + auth: flowluAuth, + name: 'flowlu_delete_task', + displayName: 'Delete Task', + description: 'Deletes an existing task.', + props: { + task_id: flowluCommon.task_id(true), + }, + async run(context) { + const task_id = context.propsValue.task_id!; + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.deleteAction( + FlowluModule.TASK, + FlowluEntity.TASKS, + task_id + ); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/tasks/get-task.ts b/packages/pieces/flowlu/src/lib/actions/tasks/get-task.ts new file mode 100644 index 000000000..129c6fafd --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/tasks/get-task.ts @@ -0,0 +1,23 @@ +import { + createAction, + PiecePropValueSchema, +} from '@activepieces/pieces-framework'; +import { flowluAuth } from '../../..'; +import { flowluCommon, makeClient } from '../../common'; + +export const getTaskAction = createAction({ + auth: flowluAuth, + name: 'flowlu_get_task', + displayName: 'Get Task', + description: 'Retrives an existing task.', + props: { + task_id: flowluCommon.task_id(true), + }, + async run(context) { + const task_id = context.propsValue.task_id!; + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.getTask(task_id); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/actions/tasks/update-task.ts b/packages/pieces/flowlu/src/lib/actions/tasks/update-task.ts new file mode 100644 index 000000000..c60ed1e7f --- /dev/null +++ b/packages/pieces/flowlu/src/lib/actions/tasks/update-task.ts @@ -0,0 +1,44 @@ +import { + PiecePropValueSchema, + createAction, +} from '@activepieces/pieces-framework'; +import dayjs from 'dayjs'; +import { flowluAuth } from '../../../'; +import { flowluCommon, makeClient } from '../../common'; +import { flowluProps } from '../../common/props'; + +export const updateTaskAction = createAction({ + auth: flowluAuth, + name: 'flowlu_update_task', + displayName: 'Update Task', + description: 'Updates an existing task.', + props: { + task_id: flowluCommon.task_id(true), + ...flowluProps.task, + }, + async run(context) { + const client = makeClient( + context.auth as PiecePropValueSchema + ); + return await client.updateTask(context.propsValue.task_id!, { + name: context.propsValue.name, + description: context.propsValue.description, + priority: context.propsValue.priority, + plan_start_date: context.propsValue.plan_start_date + ? dayjs(context.propsValue.plan_start_date).format( + 'YYYY-MM-DD HH:mm:ss' + ) + : undefined, + deadline: context.propsValue.deadline + ? dayjs(context.propsValue.deadline).format('YYYY-MM-DD HH:mm:ss') + : undefined, + deadline_allowchange: context.propsValue.deadline_allowchange ? 1 : 0, + task_checkbyowner: context.propsValue.task_checkbyowner ? 1 : 0, + type: context.propsValue.type, + responsible_id: context.propsValue.responsible_id, + owner_id: context.propsValue.owner_id, + workflow_id: context.propsValue.workflow_id, + workflow_stage_id: context.propsValue.workflow_stage_id, + }); + }, +}); diff --git a/packages/pieces/flowlu/src/lib/common/client.ts b/packages/pieces/flowlu/src/lib/common/client.ts new file mode 100644 index 000000000..767b91365 --- /dev/null +++ b/packages/pieces/flowlu/src/lib/common/client.ts @@ -0,0 +1,198 @@ +import { + HttpMessageBody, + HttpMethod, + QueryParams, + httpClient, +} from '@activepieces/pieces-common'; +import { FlowluEntity, FlowluModule } from './constants'; +import { + Account, + AccountCategory, + AccountHonorificTitle, + AccountIndustry, + CreateCRMAccountAPIRequest, + CreateOpportunityAPIRequest, + CreateTaskAPIRequest, + ListAPIResponse, + Opportunity, + OpportunitySource, + Task, + TaskWorkflow, + TaskWorkflowStage, + User, +} from './types'; + +function emptyValueFilter( + accessor: (key: string) => any +): (key: string) => boolean { + return (key: string) => { + const val = accessor(key); + return ( + val !== null && + val !== undefined && + (typeof val != 'string' || val.length > 0) + ); + }; +} + +export function prepareQuery(request?: Record): QueryParams { + const params: QueryParams = {}; + if (!request) return params; + Object.keys(request) + .filter(emptyValueFilter((k) => request[k])) + .forEach((k: string) => { + params[k] = (request as Record)[k].toString(); + }); + return params; +} + +export class FlowluClient { + constructor(private domain: string, private apiKey: string) {} + async makeRequest( + method: HttpMethod, + resourceUri: string, + query?: QueryParams, + body: any | undefined = undefined + ): Promise { + const res = await httpClient.sendRequest({ + method: method, + url: `https://${this.domain}.flowlu.com/api/v1/module` + resourceUri, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + queryParams: { api_key: this.apiKey, ...query }, + body: body, + }); + return res.body; + } + async createAccount(request: CreateCRMAccountAPIRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/crm/account/create', + undefined, + request + ); + } + async updateContact(id: number, request: CreateCRMAccountAPIRequest) { + return await this.makeRequest( + HttpMethod.POST, + `/crm/account/update/${id}`, + undefined, + request + ); + } + async listAllAccounts() { + return await this.makeRequest>( + HttpMethod.GET, + '/crm/account/list' + ); + } + async listAllContacts() { + return await this.makeRequest>( + HttpMethod.GET, + '/crm/account/list', + { 'filter[type]': '2' } + ); + } + async createTask(request: CreateTaskAPIRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/task/tasks/create', + undefined, + request + ); + } + async updateTask(id: number, request: CreateTaskAPIRequest) { + return await this.makeRequest( + HttpMethod.POST, + `/task/tasks/update/${id}`, + undefined, + request + ); + } + async getTask(id: number) { + return await this.makeRequest(HttpMethod.GET, `/task/tasks/get/${id}`); + } + async listAllTasks() { + return await this.makeRequest>( + HttpMethod.GET, + '/task/tasks/list' + ); + } + async createOpportunity(request: CreateOpportunityAPIRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/crm/lead/create', + undefined, + request + ); + } + async updateOpportunity(id: number, request: CreateOpportunityAPIRequest) { + return await this.makeRequest( + HttpMethod.POST, + `/crm/lead/update/${id}`, + undefined, + request + ); + } + async listAllOpportunities() { + return await this.makeRequest>( + HttpMethod.GET, + '/crm/lead/list' + ); + } + async listAllUsers() { + return await this.makeRequest>( + HttpMethod.GET, + '/core/user/list' + ); + } + async listAllTaskWorkflow() { + return await this.makeRequest>( + HttpMethod.GET, + '/task/workflows/list' + ); + } + async listAllTaskStages() { + return await this.makeRequest>( + HttpMethod.GET, + '/task/stages/list' + ); + } + async listAllHonorificTitles() { + return await this.makeRequest>( + HttpMethod.GET, + '/crm/honorific_title/list' + ); + } + async listAllAccountCategories() { + return await this.makeRequest>( + HttpMethod.GET, + '/crm/account_category/list' + ); + } + + async listAllAccountIndustries() { + return await this.makeRequest>( + HttpMethod.GET, + '/crm/industry/list' + ); + } + async listAllOpportunitySources() { + return await this.makeRequest>( + HttpMethod.GET, + '/crm/source/list' + ); + } + + async deleteAction( + moduleName: FlowluModule, + entityName: FlowluEntity, + id: number + ) { + return await this.makeRequest( + HttpMethod.GET, + `/${moduleName}/${entityName}/delete/${id}` + ); + } +} diff --git a/packages/pieces/flowlu/src/lib/common/constants.ts b/packages/pieces/flowlu/src/lib/common/constants.ts new file mode 100644 index 000000000..86c19def4 --- /dev/null +++ b/packages/pieces/flowlu/src/lib/common/constants.ts @@ -0,0 +1,20 @@ +export const enum FlowluModule { + TASK = 'task', + CRM = 'crm', + PROJECT = 'st', + CORE = 'core', +} +export const enum FlowluEntity { + TASKS = 'tasks', + WORKFLOWS = 'workflows', + STAGES = 'stages', + ACCOUNT = 'account', + SOURCE = 'source', + PIPELINE_STAGES = 'pipeline_stage', + PIPELINE = 'pipeline', + OPPORTUNITY = 'lead', + ACCOUNT_TYPE = 'account_category', + ACCOUNT_INDUSTRY = 'industry', + USER = 'user', + HONORIFIC_TITLE = 'honorific_title', +} diff --git a/packages/pieces/flowlu/src/lib/common/index.ts b/packages/pieces/flowlu/src/lib/common/index.ts new file mode 100644 index 000000000..f3892d4ae --- /dev/null +++ b/packages/pieces/flowlu/src/lib/common/index.ts @@ -0,0 +1,334 @@ +import { PiecePropValueSchema, Property } from '@activepieces/pieces-framework'; +import { flowluAuth } from '../..'; +import { FlowluClient } from './client'; + +export function makeClient( + auth: PiecePropValueSchema +): FlowluClient { + const client = new FlowluClient(auth.domain, auth.apiKey); + return client; +} +function mapItemsToOptions(items: { id: string | number; name: string }[]) { + return items.map((item) => ({ label: item.name, value: item.id })); +} +export const flowluCommon = { + task_id: (required = true) => + Property.Dropdown({ + displayName: 'Task ID', + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllTasks(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + user_id: (required = true, displayName = 'User ID') => + Property.Dropdown({ + displayName, + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllUsers(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + workflow_id: (required = false) => + Property.Dropdown({ + displayName: 'Task Workflow ID', + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllTaskWorkflow(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + workflow_stage_id: (required = false) => + Property.Dropdown({ + displayName: 'Task Workflow Status ID', + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllTaskStages(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + honorific_title_id: (required = false) => + Property.Dropdown({ + displayName: 'Title', + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const res = await client.listAllHonorificTitles(); + const { response } = res; + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + account_category_id: (required = false) => + Property.Dropdown({ + displayName: 'Account Category', + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllAccountCategories(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + industry_id: (required = false) => + Property.Dropdown({ + displayName: 'Account Industry', + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllAccountIndustries(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + source_id: (required = false) => + Property.Dropdown({ + displayName: 'Opportunity Source', + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllOpportunitySources(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + opportunity_id: (required = false) => + Property.Dropdown({ + displayName: 'Opportunity ID', + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllOpportunities(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + account_id: ( + required = false, + displayName = 'Account ID', + description = '' + ) => + Property.Dropdown({ + displayName, + description, + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllAccounts(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + contact_id: ( + required = false, + displayName = 'Contact ID', + description = '' + ) => + Property.Dropdown({ + displayName, + description, + required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient( + auth as PiecePropValueSchema + ); + const { response } = await client.listAllContacts(); + return { + disabled: false, + options: response.items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), +}; diff --git a/packages/pieces/flowlu/src/lib/common/props.ts b/packages/pieces/flowlu/src/lib/common/props.ts new file mode 100644 index 000000000..70a740d4e --- /dev/null +++ b/packages/pieces/flowlu/src/lib/common/props.ts @@ -0,0 +1,227 @@ +import { Property } from '@activepieces/pieces-framework'; +import { flowluCommon } from '.'; + +export const flowluProps = { + task: { + name: Property.ShortText({ + displayName: 'Name', + required: true, + }), + description: Property.LongText({ + displayName: 'Description', + required: false, + }), + priority: Property.StaticDropdown({ + displayName: 'Priority', + required: true, + options: { + disabled: false, + options: [ + { + label: 'Low', + value: 1, + }, + { + label: 'Medium', + value: 2, + }, + { + label: 'High', + value: 3, + }, + ], + }, + }), + plan_start_date: Property.DateTime({ + displayName: 'Start Date', + required: false, + }), + deadline: Property.DateTime({ + displayName: 'End Date', + required: false, + }), + deadline_allowchange: Property.Checkbox({ + displayName: 'The assignee can change the end date for this task?', + required: true, + defaultValue: false, + }), + task_checkbyowner: Property.Checkbox({ + displayName: 'This task needs approval from the owner?', + required: true, + defaultValue: false, + }), + responsible_id: flowluCommon.user_id(false, 'Assignee ID'), + owner_id: flowluCommon.user_id(false, 'Owner ID'), + type: Property.StaticDropdown({ + displayName: 'Task Type', + required: true, + defaultValue: 0, + options: { + disabled: false, + options: [ + { + label: 'Task', + value: 0, + }, + { + label: 'Inbox', + value: 1, + }, + { + label: 'Event', + value: 20, + }, + { + label: 'Task template', + value: 30, + }, + ], + }, + }), + workflow_id: flowluCommon.workflow_id(false), + workflow_stage_id: flowluCommon.workflow_stage_id(false), + }, + account: { + owner_id: flowluCommon.user_id(false, 'Assignee ID'), + account_category_id: flowluCommon.account_category_id(false), + industry_id: flowluCommon.industry_id(false), + web: Property.ShortText({ + displayName: 'Website', + required: false, + }), + email: Property.ShortText({ + displayName: 'Email', + required: false, + }), + phone: Property.ShortText({ + displayName: 'Primary Phone Number', + required: false, + }), + description: Property.LongText({ + displayName: 'Description', + required: false, + }), + vat: Property.ShortText({ + displayName: 'VAT or TAX ID', + required: false, + }), + bank_details: Property.LongText({ + displayName: 'Bank Details', + required: false, + }), + telegram: Property.ShortText({ + displayName: 'Telegram', + required: false, + }), + skype: Property.ShortText({ + displayName: 'Skype Account ID', + required: false, + }), + link_google: Property.ShortText({ + displayName: 'Link to Google+', + required: false, + }), + link_facebook: Property.ShortText({ + displayName: 'Link to Facebook', + required: false, + }), + link_linkedin: Property.ShortText({ + displayName: 'Link to Linkedin', + required: false, + }), + link_instagram: Property.ShortText({ + displayName: 'Link to Instagram', + required: false, + }), + billing_country: Property.ShortText({ + displayName: 'Billing Country', + required: false, + }), + billing_state: Property.ShortText({ + displayName: 'Billing State', + required: false, + }), + billing_city: Property.ShortText({ + displayName: 'Billing City', + required: false, + }), + billing_zip: Property.ShortText({ + displayName: 'Billing Postal code', + required: false, + }), + billing_address_line_1: Property.ShortText({ + displayName: 'Billing Address Line 1', + required: false, + }), + billing_address_line_2: Property.ShortText({ + displayName: 'Billing Address Line 2', + required: false, + }), + billing_address_line_3: Property.ShortText({ + displayName: 'Billing Address Line 3', + required: false, + }), + shipping_country: Property.ShortText({ + displayName: 'Shipping Country', + required: false, + }), + shipping_state: Property.ShortText({ + displayName: 'Shipping State', + required: false, + }), + shipping_city: Property.ShortText({ + displayName: 'Shipping City', + required: false, + }), + shipping_zip: Property.ShortText({ + displayName: 'Shipping Postal code', + required: false, + }), + shipping_address_line_1: Property.ShortText({ + displayName: 'Shipping Address Line 1', + required: false, + }), + shipping_address_line_2: Property.ShortText({ + displayName: 'Shipping Address Line 2', + required: false, + }), + shipping_address_line_3: Property.ShortText({ + displayName: 'Shipping Address Line 3', + required: false, + }), + }, + opportunity: { + name: Property.ShortText({ + displayName: 'Title', + required: true, + }), + budget: Property.Number({ + displayName: 'Opportunity Amount', + required: false, + }), + description: Property.LongText({ + displayName: 'Description', + required: false, + }), + source_id: flowluCommon.source_id(false), + start_date: Property.DateTime({ + displayName: 'Start Date', + required: false, + }), + deadline: Property.DateTime({ + displayName: 'End Date', + required: false, + }), + assignee_id: flowluCommon.user_id(false, 'Assignee ID'), + customer_id: flowluCommon.account_id( + false, + 'Customer ID', + `This is an id of the CRM company or contact which is needed to be linked with the opportunity. This allows you to link the client to the opportunity. If your client is a company, and you need to relate an opportunity to the person (contact) at this company, then enter his/her id in the contact_id field.` + ), + contact_id: flowluCommon.contact_id( + false, + 'Contact ID', + `Id of the company-related contact (account_id).` + ), + }, +}; diff --git a/packages/pieces/flowlu/src/lib/common/types.ts b/packages/pieces/flowlu/src/lib/common/types.ts new file mode 100644 index 000000000..e40ffb114 --- /dev/null +++ b/packages/pieces/flowlu/src/lib/common/types.ts @@ -0,0 +1,211 @@ +import { HttpMessageBody } from '@activepieces/pieces-common'; + +export interface Task { + id: number; + name: string; + description: string; + report: string; + parent_id: number; + prev_task_id: number; + deadline: string; + deadline_allowchange: number; + plan_start_date: string; + plan_end_date: string; + priority: number; + task_checkbyowner: number; + responsible_id: number; + time_estimate: number; + time_spent: number; + status_firstviewdate: string; + start_date: string; + closed_date: string; + first_closed_date: string; + closed_by: number; + return_count: number; + rating: number; + ref: string; + ref_id: string; + module: string; + model: string; + model_id: number; + type: number; + report_complete: string; + all_day: number; + ordering: number; + uuid: string; + public_template: number; + template_id: number; + is_repeat: number; + event_location: string; + event_color: string; + event_busy_status: number; + event_access_type: number; + event_calendar_id: number; + event_type: number; + event_etag: string; + event_sync_type: number; + extra_fields: string; + archive_status: number; + is_hidden: number; + workflow_id: number; + workflow_stage_id: number; + deleted_at: null; + is_archive: string; + created_date: string; + owner_id: number; + updated_date: string; + updated_by: number; + status_updated_date: string; + status_updated_by: number; + project_stage_id: number; + project_checkitem_id: number; + crm_account_id: number; +} +export interface User { + id: string; + last_active: string; + username: string; + first_name: string; + second_name: string; + last_name: string; + birth_date: string; + lang_id: string; + timezone: string; + register_date: string; + image: string; + role_admin: number; + role_login: number; + name: string; +} +export interface Account { + id: number; + name: string; + type: number; +} +export interface TaskWorkflow { + id: number; + name: string; + description: string; + ordering: number; + created_by: number; + updated_by: number; + updated_date: string; + active: number; + deleted_at: string; +} +export interface TaskWorkflowStage { + id: number; + name: string; + description: string; + ordering: number; + created_by: number; + updated_by: number; + updated_date: string; + workflow_id: number; + color: string; + task_status: number; + deleted_at: string; +} +export interface ListAPIResponse extends HttpMessageBody { + response: { + total: number; + total_result: number; + page: number; + count: number; + items: T; + }; +} +export interface AccountHonorificTitle { + id: number; + name: string; + ordering: number; + active: number; +} +export interface AccountCategory { + id: number; + active: number; + ordering: number; + name: string; + deleted_at: string; +} +export interface AccountIndustry { + id: number; + name: string; + ordering: number; + active: number; + deleted_at: string; +} +export interface OpportunitySource { + id: number; + name: string; + ordering: number; + active: number; + description: string; + deleted_at: string; +} +export interface Opportunity { + id: number; + name: string; +} +export interface CreateTaskAPIRequest { + name: string; + description?: string; + priority: number; + plan_start_date?: string; + deadline?: string; + deadline_allowchange: number; + task_checkbyowner: number; + responsible_id?: string; + owner_id?: string; + type: number; + workflow_id?: number; + workflow_stage_id?: number; +} + +export interface CreateCRMAccountAPIRequest { + type: number; + name_legal_full?: string; + first_name?: string; + middle_name?: string; + last_name?: string; + owner_id?: string; + account_category_id?: number; + industry_id?: number; + web?: string; + email?: string; + phone?: string; + description?: string; + vat?: string; + bank_details?: string; + telegram?: string; + skype?: string; + link_google?: string; + link_facebook?: string; + link_linkedin?: string; + link_instagram?: string; + billing_country?: string; + billing_state?: string; + billing_city?: string; + billing_zip?: string; + billing_address_line_1?: string; + billing_address_line_2?: string; + billing_address_line_3?: string; + shipping_country?: string; + shipping_state?: string; + shipping_city?: string; + shipping_zip?: string; + shipping_address_line_1?: string; + shipping_address_line_2?: string; + shipping_address_line_3?: string; +} +export interface CreateOpportunityAPIRequest { + name: string; + budget?: number; + description?: string; + source_id?: number; + start_date?: string; + deadline?: string; + assignee_id?: string; + customer_id?: number; + contact_id?: number; +} diff --git a/packages/pieces/flowlu/tsconfig.json b/packages/pieces/flowlu/tsconfig.json new file mode 100644 index 000000000..f2400abed --- /dev/null +++ b/packages/pieces/flowlu/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/pieces/flowlu/tsconfig.lib.json b/packages/pieces/flowlu/tsconfig.lib.json new file mode 100644 index 000000000..e583571ea --- /dev/null +++ b/packages/pieces/flowlu/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/packages/pieces/framework/package.json b/packages/pieces/framework/package.json index 817ee0670..131e0e6a9 100644 --- a/packages/pieces/framework/package.json +++ b/packages/pieces/framework/package.json @@ -1,5 +1,5 @@ { "name": "@activepieces/pieces-framework", - "version": "0.7.4", + "version": "0.7.10", "type": "commonjs" } diff --git a/packages/pieces/framework/src/lib/context.ts b/packages/pieces/framework/src/lib/context.ts index 07834f5cd..443d4ff08 100644 --- a/packages/pieces/framework/src/lib/context.ts +++ b/packages/pieces/framework/src/lib/context.ts @@ -1,4 +1,4 @@ -import { AppConnectionValue, ExecutionType, FlowRunId, PauseMetadata, StopResponse, TriggerPayload } from "@activepieces/shared"; +import { AppConnectionValue, ExecutionType, FlowRunId, PauseMetadata, ProjectId, StopResponse, TriggerPayload } from "@activepieces/shared"; import { TriggerStrategy } from "./trigger/trigger"; import { NonAuthPiecePropertyMap, PieceAuthProperty, PiecePropValueSchema, PiecePropertyMap, StaticPropsValue } from "./property"; @@ -6,6 +6,10 @@ type BaseContext, propsValue: StaticPropsValue store: Store + project: { + id: ProjectId, + externalId: () => Promise + } } type AppWebhookTriggerHookContext = diff --git a/packages/pieces/framework/src/lib/index.ts b/packages/pieces/framework/src/lib/index.ts index 8083b7834..d97ffa190 100644 --- a/packages/pieces/framework/src/lib/index.ts +++ b/packages/pieces/framework/src/lib/index.ts @@ -6,3 +6,4 @@ export * from './piece' export * from './piece-metadata' export * from './validators/validators' export * from './processors/processors' +export * from './processors/types' diff --git a/packages/pieces/framework/src/lib/piece-metadata.ts b/packages/pieces/framework/src/lib/piece-metadata.ts index 718aceaa0..007cf5630 100644 --- a/packages/pieces/framework/src/lib/piece-metadata.ts +++ b/packages/pieces/framework/src/lib/piece-metadata.ts @@ -26,7 +26,7 @@ export type ActionBase = { requireAuth: boolean; } -export type TriggerBase = ActionBase & { +export type TriggerBase = Omit & { type: TriggerStrategy; sampleData: unknown, handshakeConfiguration?: WebhookHandshakeConfiguration; diff --git a/packages/pieces/framework/src/lib/processors/processors.ts b/packages/pieces/framework/src/lib/processors/processors.ts index e5e8e54b0..f07faa23f 100644 --- a/packages/pieces/framework/src/lib/processors/processors.ts +++ b/packages/pieces/framework/src/lib/processors/processors.ts @@ -10,6 +10,21 @@ import { isNil, isString } from "@activepieces/shared"; export class Processors { + static json: ProcessorFn = (property, value) => { + if (isNil(value)) { + return value; + } + try { + if (typeof value === 'object') { + return value; + } + return JSON.parse(value); + } catch (error) { + console.error(error); + return undefined; + } + } + static number: ProcessorFn = (property, value) => { if (isNil(value)) { return value; diff --git a/packages/pieces/framework/src/lib/property/property.ts b/packages/pieces/framework/src/lib/property/property.ts index ed213ee19..60c37dde9 100644 --- a/packages/pieces/framework/src/lib/property/property.ts +++ b/packages/pieces/framework/src/lib/property/property.ts @@ -110,7 +110,7 @@ export const Property = { }, Json(request: Properties>): R extends true ? JsonProperty : JsonProperty { - return { ...request, valueSchema: undefined, type: PropertyType.JSON } as unknown as R extends true ? JsonProperty : JsonProperty; + return { ...request, valueSchema: undefined, type: PropertyType.JSON, defaultProcessors: [Processors.json] } as unknown as R extends true ? JsonProperty : JsonProperty; }, Array(request: Properties>): R extends true ? ArrayProperty : ArrayProperty { return { ...request, valueSchema: undefined, type: PropertyType.ARRAY } as unknown as R extends true ? ArrayProperty : ArrayProperty; diff --git a/packages/pieces/framework/src/lib/trigger/trigger.ts b/packages/pieces/framework/src/lib/trigger/trigger.ts index d556684ad..183c8bf81 100644 --- a/packages/pieces/framework/src/lib/trigger/trigger.ts +++ b/packages/pieces/framework/src/lib/trigger/trigger.ts @@ -46,7 +46,6 @@ type CreateTriggerParams< onDisable: (context: TriggerHookContext) => Promise run: (context: TestOrRunHookContext) => Promise test?: (context: TestOrRunHookContext) => Promise - requireAuth?: boolean sampleData: unknown } @@ -68,7 +67,6 @@ export class ITrigger< public readonly run: (ctx: TestOrRunHookContext) => Promise, public readonly test: (ctx: TestOrRunHookContext) => Promise, public sampleData: unknown, - public readonly requireAuth: boolean = true, ) { } } @@ -96,6 +94,5 @@ export const createTrigger = < params.run, params.test ?? (() => Promise.resolve([params.sampleData])), params.sampleData, - params.requireAuth, ) } diff --git a/packages/pieces/gmail/package.json b/packages/pieces/gmail/package.json index 4284d2e5a..170220310 100644 --- a/packages/pieces/gmail/package.json +++ b/packages/pieces/gmail/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-gmail", - "version": "0.4.4" + "version": "0.4.5" } diff --git a/packages/pieces/gmail/src/index.ts b/packages/pieces/gmail/src/index.ts index dbeafb93c..766fd5de3 100644 --- a/packages/pieces/gmail/src/index.ts +++ b/packages/pieces/gmail/src/index.ts @@ -15,7 +15,7 @@ export const gmail = createPiece({ logoUrl: 'https://cdn.activepieces.com/pieces/gmail.png', actions: [gmailSendEmailAction], displayName: 'Gmail', - authors: ['AbdulTheActivePiecer', 'kanarelo', 'BastienMe'], + authors: ['AbdulTheActivePiecer', 'kanarelo', 'BastienMe', 'PFernandez98'], triggers: [], auth: gmailAuth, }); diff --git a/packages/pieces/gmail/src/lib/actions/send-email-action.ts b/packages/pieces/gmail/src/lib/actions/send-email-action.ts index 32dbf91fe..cca78dcff 100644 --- a/packages/pieces/gmail/src/lib/actions/send-email-action.ts +++ b/packages/pieces/gmail/src/lib/actions/send-email-action.ts @@ -55,10 +55,16 @@ export const gmailSendEmailAction = createAction({ description: 'File to attach to the email you want to send', required: false, }), + attachment_name: Property.ShortText({ + displayName: 'Attachment Name', + description: 'In case you want to change the name of the attachment', + required: false, + }), }, async run(configValue) { const subjectBase64 = Buffer.from(configValue.propsValue['subject']).toString("base64"); const attachment = configValue.propsValue['attachment']; + const attachment_name = configValue.propsValue['attachment_name']; const replyTo = configValue.propsValue['reply_to']?.filter((email) => email !== ''); const reciever = configValue.propsValue['receiver']?.filter((email) => email !== ''); const cc = configValue.propsValue['cc']?.filter((email) => email !== ''); @@ -81,7 +87,7 @@ export const gmailSendEmailAction = createAction({ if (attachment) { const lookupResult = mime.lookup(attachment?.extension ? attachment?.extension : ''); const attachmentOption: Attachment[] = [{ - filename: attachment?.filename, + filename: attachment_name ? attachment_name : attachment?.filename, content: attachment?.base64, contentType: lookupResult ? lookupResult : undefined, encoding: 'base64', diff --git a/packages/pieces/google-docs/package.json b/packages/pieces/google-docs/package.json index e64202fa0..d234a0870 100644 --- a/packages/pieces/google-docs/package.json +++ b/packages/pieces/google-docs/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-google-docs", - "version": "0.1.0" + "version": "0.1.2" } diff --git a/packages/pieces/google-docs/src/index.ts b/packages/pieces/google-docs/src/index.ts index 8e9da061c..29ecdd2a9 100644 --- a/packages/pieces/google-docs/src/index.ts +++ b/packages/pieces/google-docs/src/index.ts @@ -1,6 +1,8 @@ import { PieceAuth, createPiece } from "@activepieces/pieces-framework"; import { createDocument } from "./lib/actions/create-document"; +import { createDocumentBasedOnTemplate } from "./lib/actions/create-document-based-on-template.action"; +import { readDocument } from "./lib/actions/read-document.action"; export const googleDocsAuth = PieceAuth.OAuth2({ @@ -14,8 +16,8 @@ export const googleDocs = createPiece({ displayName: "Google Docs", minimumSupportedRelease: '0.5.0', logoUrl: "https://cdn.activepieces.com/pieces/google-docs.png", - authors: ['MoShizzle'], + authors: ['MoShizzle', 'PFernandez98'], auth: googleDocsAuth, - actions: [createDocument], + actions: [createDocument, createDocumentBasedOnTemplate, readDocument], triggers: [], }); diff --git a/packages/pieces/google-docs/src/lib/actions/create-document-based-on-template.action.ts b/packages/pieces/google-docs/src/lib/actions/create-document-based-on-template.action.ts new file mode 100644 index 000000000..949da3d16 --- /dev/null +++ b/packages/pieces/google-docs/src/lib/actions/create-document-based-on-template.action.ts @@ -0,0 +1,75 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { googleDocsAuth } from '../../index'; +import { Property, createAction } from "@activepieces/pieces-framework"; +import { google } from 'googleapis'; +import { OAuth2Client } from 'googleapis-common'; + + +export const createDocumentBasedOnTemplate = createAction({ + auth: googleDocsAuth, + name: 'create_document_based_on_template', + description: 'Edit a template file and replace the values with the ones provided', + displayName: 'Edit template file', + props: { + template: Property.ShortText({ + displayName: 'Destination File', + description: 'The ID of the file to replace the values', + required: true, + }), + values: Property.Object({ + displayName: 'Varibles', + description: 'Dont include the "[[]]", only the key name and its value', + required: true, + }), + images: Property.Object({ + displayName: 'Images', + description: 'Key: Image ID (get it manually from the Read File Action), Value: Image URL', + required: true, + }), + }, + async run(context) { + + const documentId: string = context.propsValue.template; + const values = context.propsValue.values; + + const authClient = new OAuth2Client(); + authClient.setCredentials(context.auth) + const docs = google.docs('v1'); + + const requests = []; + + for (const key in values) { + const value = values[key]; + requests.push({ + replaceAllText: { + containsText: { + text: "[[" + key + "]]", + matchCase: true, + }, + replaceText: String(value), + }, + }); + } + + for (const key in context.propsValue.images) { + const value = context.propsValue.images[key]; + requests.push({ + replaceImage: { + imageObjectId: key, + uri: String(value), + }, + }); + } + + const res = await docs.documents.batchUpdate({ + auth: authClient, + documentId, + requestBody: { + requests: requests, + } + + }); + + return res; + } +}); \ No newline at end of file diff --git a/packages/pieces/google-docs/src/lib/actions/read-document.action.ts b/packages/pieces/google-docs/src/lib/actions/read-document.action.ts new file mode 100644 index 000000000..53aaea43c --- /dev/null +++ b/packages/pieces/google-docs/src/lib/actions/read-document.action.ts @@ -0,0 +1,35 @@ + +import { googleDocsAuth } from '../../index'; +import { Property, createAction } from "@activepieces/pieces-framework"; +import { google } from 'googleapis'; +import { OAuth2Client } from 'googleapis-common'; + +export const readDocument = createAction({ + displayName: "Read Document", + auth: googleDocsAuth, + name: "read_document", + description: "Read a document from Google Docs", + props: { + documentId: Property.ShortText({ + displayName: "Document ID", + description: "The ID of the document to read", + required: true, + }), + }, + async run(context) { + const authClient = new OAuth2Client(); + authClient.setCredentials(context.auth); + + const docs = google.docs({ version: 'v1', auth: authClient }); + const response = await docs.documents.get({ + documentId: context.propsValue.documentId, + }); + + if (response.status !== 200) { + console.error(response); + throw new Error('Error reading document'); + } + + return response.data; + }, +}); \ No newline at end of file diff --git a/packages/pieces/google-docs/src/lib/common/index.ts b/packages/pieces/google-docs/src/lib/common/index.ts index e2c2b2294..45d59a307 100644 --- a/packages/pieces/google-docs/src/lib/common/index.ts +++ b/packages/pieces/google-docs/src/lib/common/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { Property } from "@activepieces/pieces-framework"; import { HttpMethod, httpClient, AuthenticationType } from "@activepieces/pieces-common"; @@ -51,5 +52,5 @@ export const docsCommon = { }) return writeRequest.body; - } + }, } diff --git a/packages/pieces/google-drive/package.json b/packages/pieces/google-drive/package.json index 43e918d6f..ffd3c2123 100644 --- a/packages/pieces/google-drive/package.json +++ b/packages/pieces/google-drive/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-google-drive", - "version": "0.5.5" + "version": "0.5.9" } diff --git a/packages/pieces/google-drive/src/index.ts b/packages/pieces/google-drive/src/index.ts index 6b54d358a..1acc10fe1 100644 --- a/packages/pieces/google-drive/src/index.ts +++ b/packages/pieces/google-drive/src/index.ts @@ -7,6 +7,9 @@ import { newFile } from './lib/triggers/new-file'; import { newFolder } from './lib/triggers/new-folder'; import { readFile } from './lib/action/read-file'; import { googleDriveListFiles } from './lib/action/list-files.action'; +import { googleDriveSearchFolder } from './lib/action/search-folder.action'; +import { duplicateFileAction } from './lib/action/duplicate-file.action'; +import { saveFileAsPdf } from './lib/action/save-file-as-pdf.action'; export const googleDriveAuth = PieceAuth.OAuth2({ description: "", @@ -17,11 +20,11 @@ export const googleDriveAuth = PieceAuth.OAuth2({ }) export const googleDrive = createPiece({ - minimumSupportedRelease: '0.5.0', + minimumSupportedRelease: '0.5.6', logoUrl: 'https://cdn.activepieces.com/pieces/google-drive.png', - actions: [googleDriveCreateNewFolder, googleDriveCreateNewTextFile, googleDriveUploadFile, readFile, googleDriveListFiles], displayName: "Google Drive", - authors: ['kanarelo', 'BastienMe', 'MoShizzle', 'Armangiau', 'vitalini', 'pfernandez98'], + authors: ['kanarelo', 'BastienMe', 'MoShizzle', 'Armangiau', 'vitalini', 'PFernandez98'], triggers: [newFile, newFolder], + actions: [googleDriveCreateNewFolder, googleDriveCreateNewTextFile, googleDriveUploadFile, readFile, googleDriveListFiles, googleDriveSearchFolder, duplicateFileAction, saveFileAsPdf], auth: googleDriveAuth, }); diff --git a/packages/pieces/google-drive/src/lib/action/duplicate-file.action.ts b/packages/pieces/google-drive/src/lib/action/duplicate-file.action.ts new file mode 100644 index 000000000..562ede617 --- /dev/null +++ b/packages/pieces/google-drive/src/lib/action/duplicate-file.action.ts @@ -0,0 +1,51 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { google } from 'googleapis'; +import { OAuth2Client } from 'googleapis-common'; +import { googleDriveAuth } from '../../index'; + +export const duplicateFileAction = createAction({ + displayName: 'Duplicate File', + auth: googleDriveAuth, + name: 'duplicate_file', + description: 'Duplicate a file from Google Drive. Returns the new file ID.', + props: { + fileId: Property.ShortText({ + displayName: 'File ID', + description: 'The ID of the file to duplicate', + required: true, + }), + name: Property.ShortText({ + displayName: 'Name', + description: 'The name of the new file', + required: true, + }), + folderId: Property.ShortText({ + displayName: 'Folder ID', + description: 'The ID of the folder where the file will be duplicated', + required: true, + }), + }, + async run(context) { + const authClient = new OAuth2Client(); + authClient.setCredentials(context.auth); + + const fileId = context.propsValue.fileId; + const nameForNewFile = context.propsValue.name; + const parentFolderId = context.propsValue.folderId; + + const drive = google.drive({ version: 'v3', auth: authClient }); + + const response = await drive.files.copy({ + fileId, + auth: authClient, + requestBody: { name: nameForNewFile, parents: [parentFolderId] }, + }); + + if (response.status !== 200) { + console.error(response); + throw new Error('Error duplicating file'); + } + + return response.data; + }, +}); diff --git a/packages/pieces/google-drive/src/lib/action/save-file-as-pdf.action.ts b/packages/pieces/google-drive/src/lib/action/save-file-as-pdf.action.ts new file mode 100644 index 000000000..bae668de4 --- /dev/null +++ b/packages/pieces/google-drive/src/lib/action/save-file-as-pdf.action.ts @@ -0,0 +1,69 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { googleDriveAuth } from '../../index'; +import { Property, createAction } from "@activepieces/pieces-framework"; +import { google } from 'googleapis'; +import { OAuth2Client } from 'googleapis-common'; +import { Stream } from 'stream'; + +export const saveFileAsPdf = createAction({ + displayName: "Save file as PDF", + auth: googleDriveAuth, + name: "save_file_as_pdf", + description: "Save a file as PDF in a Google Drive folder", + props: { + documentId: Property.ShortText({ + displayName: "Document ID", + description: "The ID of the document to export", + required: true, + }), + folderId: Property.ShortText({ + displayName: "Folder ID", + description: "The ID of the folder where the file will be exported", + required: true, + }), + name: Property.ShortText({ + displayName: "Name", + description: "The name of the new file (do not include the extension)", + required: true, + }), + }, + async run(context) { + const authClient = new OAuth2Client(); + authClient.setCredentials(context.auth) + + const documentId = context.propsValue.documentId; + const folderId = context.propsValue.folderId; + const nameForNewFile = context.propsValue.name; + + const drive = google.drive({ version: 'v3', auth: authClient }); + + const result = await drive.files.export({ + fileId: documentId, + mimeType: 'application/pdf', + + }, {responseType: "arraybuffer"}); + + const requestBody = { + name: nameForNewFile + ".pdf", + parents: [folderId], + }; + const templateBuffer = Buffer.from(result.data as any, 'base64'); + + const streamm = new Stream.PassThrough().end(templateBuffer); + + const media = { + mimeType: 'application/pdf', + body: streamm, + }; + + const file = await drive.files.create({ + requestBody, + media: media, + }); + + return file.data; + + + }, +}); \ No newline at end of file diff --git a/packages/pieces/google-drive/src/lib/action/search-folder.action.ts b/packages/pieces/google-drive/src/lib/action/search-folder.action.ts new file mode 100644 index 000000000..e39270145 --- /dev/null +++ b/packages/pieces/google-drive/src/lib/action/search-folder.action.ts @@ -0,0 +1,47 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { googleDriveAuth } from '../../index'; +import { Property, createAction } from "@activepieces/pieces-framework"; +import { google } from 'googleapis'; +import { OAuth2Client } from 'googleapis-common'; + +export const googleDriveSearchFolder = createAction({ + auth: googleDriveAuth, + name: 'search-folder', + displayName: 'Search folder', + description: 'Search folder from a Google Drive folder', + props: { + query: Property.ShortText({ + displayName: 'Name', + description: 'Name to search for', + required: true, + }), + }, + async run (context) { + + const authClient = new OAuth2Client(); + authClient.setCredentials(context.auth) + + const drive = google.drive({ version: 'v3', auth: authClient }); + const query = `name contains '${context.propsValue.query}' and mimeType='application/vnd.google-apps.folder'`; + + + const response = await drive.files.list({ + q: query, + fields: 'files(id, name)' + }); + + if (response.status !== 200) { + console.error(response); + throw new Error('Error searching folder'); + } + + const folders = response.data.files ?? []; + + if (folders.length > 0) { + return folders; + } else { + console.log('Folder not found'); + return []; + } + } +}); diff --git a/packages/pieces/hubspot/package.json b/packages/pieces/hubspot/package.json index 95a69f0ef..e414247e5 100644 --- a/packages/pieces/hubspot/package.json +++ b/packages/pieces/hubspot/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-hubspot", - "version": "0.4.1" + "version": "0.5.1" } diff --git a/packages/pieces/hubspot/src/index.ts b/packages/pieces/hubspot/src/index.ts index f24d81376..d30d201a5 100644 --- a/packages/pieces/hubspot/src/index.ts +++ b/packages/pieces/hubspot/src/index.ts @@ -2,41 +2,48 @@ import { PieceAuth, createPiece } from '@activepieces/pieces-framework'; import { hubSpotListsAddContactAction } from './lib/actions/add-contact-to-list-action'; import { createHubspotContact } from './lib/actions/create-contact.action'; import { hubSpotContactsCreateOrUpdateAction } from './lib/actions/create-or-update-contact-action'; -import { newTaskAdded } from './lib/triggers/new-task-added' +import { hubSpotGetOwnerByEmailAction } from './lib/actions/search-owner-by-email'; import { newCompanyAdded } from './lib/triggers/new-company-added'; import { newContactAdded } from './lib/triggers/new-contact-added'; import { newDealAdded } from './lib/triggers/new-deal-added'; +import { newTaskAdded } from './lib/triggers/new-task-added'; import { newTicketAdded } from './lib/triggers/new-ticket-added'; export const hubspotAuth = PieceAuth.OAuth2({ - - authUrl: 'https://app.hubspot.com/oauth/authorize', - tokenUrl: 'https://api.hubapi.com/oauth/v1/token', - required: true, - scope: [ - 'crm.lists.read', - 'crm.lists.write', - 'crm.objects.contacts.read', - 'crm.objects.contacts.write', - ], + authUrl: 'https://app.hubspot.com/oauth/authorize', + tokenUrl: 'https://api.hubapi.com/oauth/v1/token', + required: true, + scope: [ + 'crm.lists.read', + 'crm.lists.write', + 'crm.objects.contacts.read', + 'crm.objects.contacts.write', + 'crm.objects.owners.read', + 'crm.objects.companies.read', + 'crm.objects.companies.write', + 'crm.objects.deals.read', + 'crm.objects.deals.write', + 'tickets', + ], }); export const hubspot = createPiece({ - displayName: "HubSpot", - minimumSupportedRelease: '0.5.0', - logoUrl: 'https://cdn.activepieces.com/pieces/hubspot.png', - authors: ['khaledmashaly', 'MoShizzle','Salem-Alaa'], - auth: hubspotAuth, - actions: [ - createHubspotContact, - hubSpotContactsCreateOrUpdateAction, - hubSpotListsAddContactAction, - ], - triggers: [ - newTaskAdded, - newCompanyAdded, - newContactAdded, - newDealAdded, - newTicketAdded - ], + displayName: 'HubSpot', + minimumSupportedRelease: '0.5.0', + logoUrl: 'https://cdn.activepieces.com/pieces/hubspot.png', + authors: ['khaledmashaly', 'MoShizzle', 'Salem-Alaa', 'kishanprmr'], + auth: hubspotAuth, + actions: [ + createHubspotContact, + hubSpotContactsCreateOrUpdateAction, + hubSpotListsAddContactAction, + hubSpotGetOwnerByEmailAction, + ], + triggers: [ + newTaskAdded, + newCompanyAdded, + newContactAdded, + newDealAdded, + newTicketAdded, + ], }); diff --git a/packages/pieces/hubspot/src/lib/actions/search-owner-by-email.ts b/packages/pieces/hubspot/src/lib/actions/search-owner-by-email.ts new file mode 100644 index 000000000..c5249e0b8 --- /dev/null +++ b/packages/pieces/hubspot/src/lib/actions/search-owner-by-email.ts @@ -0,0 +1,23 @@ +import { createAction, Property } from '@activepieces/pieces-framework'; +import { hubspotAuth } from '../../'; +import { hubSpotClient } from '../common/client'; + +export const hubSpotGetOwnerByEmailAction = createAction({ + auth: hubspotAuth, + name: 'get_owner_by_email', + displayName: 'Get Owner by Email', + description: 'Retrieves an existing owner by email.', + props: { + email: Property.ShortText({ + displayName: 'Owner Email', + required: true, + }), + }, + async run(context) { + const { email } = context.propsValue; + return await hubSpotClient.listContactOwners( + context.auth.access_token as string, + email as string + ); + }, +}); diff --git a/packages/pieces/hubspot/src/lib/common/client.ts b/packages/pieces/hubspot/src/lib/common/client.ts index 4e5fbfa08..71888ae05 100644 --- a/packages/pieces/hubspot/src/lib/common/client.ts +++ b/packages/pieces/hubspot/src/lib/common/client.ts @@ -1,271 +1,332 @@ -import { HttpRequest, HttpMethod, AuthenticationType, httpClient } from '@activepieces/pieces-common'; -import { Contact, HubSpotAddContactsToListRequest, HubSpotAddContactsToListResponse, HubSpotContactsCreateOrUpdateResponse, HubSpotListsResponse, HubSpotRequest } from './models'; +import { + AuthenticationType, + HttpMethod, + HttpRequest, + httpClient, +} from '@activepieces/pieces-common'; +import { + Contact, + HubSpotAddContactsToListRequest, + HubSpotAddContactsToListResponse, + HubSpotContactsCreateOrUpdateResponse, + HubSpotListsResponse, + HubSpotRequest, +} from './models'; const API = 'https://api.hubapi.com'; export const hubSpotClient = { - contacts: { - async createOrUpdate({ token, email, contact }: ContactsCreateOrUpdateParams): Promise { - const properties = Object.entries(contact).map(([property, value]) => ({ - property, - value, - })); + contacts: { + async createOrUpdate({ + token, + email, + contact, + }: ContactsCreateOrUpdateParams): Promise { + const properties = Object.entries(contact).map(([property, value]) => ({ + property, + value, + })); - const request: HttpRequest = { - method: HttpMethod.POST, - url: `${API}/contacts/v1/contact/createOrUpdate/email/${email}`, - body: { - properties, - }, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token, - }, - }; - - const response = await httpClient.sendRequest(request); - return response.body; + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${API}/contacts/v1/contact/createOrUpdate/email/${email}`, + body: { + properties, }, - }, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token, + }, + }; - lists: { - async getStaticLists({ token }: GetStaticListsParams): Promise { - const request: HttpRequest = { - method: HttpMethod.GET, - url: `${API}/contacts/v1/lists/static`, - queryParams: { - count: '250', - offset: '0', - }, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token, - }, - }; + const response = + await httpClient.sendRequest( + request + ); + return response.body; + }, + }, - const response = await httpClient.sendRequest(request); - return response.body; + lists: { + async getStaticLists({ + token, + }: GetStaticListsParams): Promise { + const request: HttpRequest = { + method: HttpMethod.GET, + url: `${API}/contacts/v1/lists/static`, + queryParams: { + count: '250', + offset: '0', }, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token, + }, + }; + + const response = await httpClient.sendRequest( + request + ); + return response.body; + }, - async addContact({ token, listId, email }: ListsAddContactParams): Promise { - const request: HttpRequest = { - method: HttpMethod.POST, - url: `${API}/contacts/v1/lists/${listId}/add`, - body: { - emails: [email], - }, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token, - }, - }; + async addContact({ + token, + listId, + email, + }: ListsAddContactParams): Promise { + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${API}/contacts/v1/lists/${listId}/add`, + body: { + emails: [email], + }, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token, + }, + }; - const response = await httpClient.sendRequest(request); - return response.body; - } + const response = + await httpClient.sendRequest(request); + return response.body; }, + }, - tasks: { - async getTasksAfterLastSearch(accessToken: string, lastFetchEpochMS: number) { - const request: HttpRequest = { - method: HttpMethod.POST, - url: `${API}/crm/v3/objects/tasks/search`, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: accessToken, - }, - headers: { - "Content-Type": 'application/json' - }, - body: { - filters: [ - { - propertyName: "hs_createdate", - operator: "GT", - value: lastFetchEpochMS - } - ], - properties: [ - 'hs_task_body', - 'hubspot_owner_id', - 'hs_task_subject', - 'hs_task_status', - 'hs_task_priority', - 'hs_task_type', - 'hs_created_by', - 'hs_created_by_user_id', - 'hs_modified_by', - 'hs_num_associated_companies', - 'hs_num_associated_contacts', - 'hs_num_associated_deals', - 'hs_num_associated_tickets', - 'hs_product_name', - 'hs_read_only', - 'hs_repeat_status', - 'hs_task_completion_count', - 'hs_task_completion_date', - 'hs_task_is_all_day', - 'hs_task_is_completed', - 'hs_task_is_completed_call', - 'hs_task_is_completed_email', - 'hs_task_is_completed_linked_in', - 'hs_task_is_completed_sequence', - 'hs_task_repeat_interval', - 'hs_updated_by_user_id', - 'hs_timestamp' - ], - limit: 100 - } - }; + tasks: { + async getTasksAfterLastSearch( + accessToken: string, + lastFetchEpochMS: number + ) { + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${API}/crm/v3/objects/tasks/search`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: accessToken, + }, + headers: { + 'Content-Type': 'application/json', + }, + body: { + filters: [ + { + propertyName: 'hs_createdate', + operator: 'GT', + value: lastFetchEpochMS, + }, + ], + properties: [ + 'hs_task_body', + 'hubspot_owner_id', + 'hs_task_subject', + 'hs_task_status', + 'hs_task_priority', + 'hs_task_type', + 'hs_created_by', + 'hs_created_by_user_id', + 'hs_modified_by', + 'hs_num_associated_companies', + 'hs_num_associated_contacts', + 'hs_num_associated_deals', + 'hs_num_associated_tickets', + 'hs_product_name', + 'hs_read_only', + 'hs_repeat_status', + 'hs_task_completion_count', + 'hs_task_completion_date', + 'hs_task_is_all_day', + 'hs_task_is_completed', + 'hs_task_is_completed_call', + 'hs_task_is_completed_email', + 'hs_task_is_completed_linked_in', + 'hs_task_is_completed_sequence', + 'hs_task_repeat_interval', + 'hs_updated_by_user_id', + 'hs_timestamp', + ], + limit: 100, + }, + }; - const response = await httpClient.sendRequest(request); - return response.body; - } + const response = await httpClient.sendRequest(request); + return response.body; }, + }, - async searchCompanies(accessToken: string, filters?: { - createdAt?: number, - createdAtOperator?: string - }) { - const searchParams = []; + async searchCompanies( + accessToken: string, + filters?: { + createdAt?: number; + createdAtOperator?: string; + } + ) { + const searchParams = []; - if (filters && filters.createdAt) { - searchParams.push({ - propertyName: 'createdate', - operator: filters.createdAtOperator ?? 'GT', - value: filters.createdAt - }) - } + if (filters && filters.createdAt) { + searchParams.push({ + propertyName: 'createdate', + operator: filters.createdAtOperator ?? 'GT', + value: filters.createdAt, + }); + } - const response = await httpClient.sendRequest({ - method: HttpMethod.POST, - url: `${API}/crm/v3/objects/companies/search`, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: accessToken, - }, - headers: { - "Content-Type": 'application/json' - }, - body: { - filters: searchParams - } - }); + const response = await httpClient.sendRequest({ + method: HttpMethod.POST, + url: `${API}/crm/v3/objects/companies/search`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: accessToken, + }, + headers: { + 'Content-Type': 'application/json', + }, + body: { + filters: searchParams, + }, + }); - return response.body; - }, + return response.body; + }, - async searchContacts(accessToken: string , wantedFields: string[] , filters?: { - createdAt?: number, - createdAtOperator?: string - }) { - const searchParams = []; + async searchContacts( + accessToken: string, + wantedFields: string[], + filters?: { + createdAt?: number; + createdAtOperator?: string; + } + ) { + const searchParams = []; - if (filters && filters.createdAt) { - searchParams.push({ - propertyName: 'createdate', - operator: filters.createdAtOperator ?? 'GT', - value: filters.createdAt - }) - } - const requestBody : Record = { - filters: searchParams, - properties: wantedFields - }; - - const response = await httpClient.sendRequest({ - method: HttpMethod.POST, - url: `${API}/crm/v3/objects/contacts/search`, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: accessToken, - }, - headers: { - "Content-Type": 'application/json' - }, - body: requestBody - }); + if (filters && filters.createdAt) { + searchParams.push({ + propertyName: 'createdate', + operator: filters.createdAtOperator ?? 'GT', + value: filters.createdAt, + }); + } + const requestBody: Record = { + filters: searchParams, + properties: wantedFields, + }; - return response.body; - }, + const response = await httpClient.sendRequest({ + method: HttpMethod.POST, + url: `${API}/crm/v3/objects/contacts/search`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: accessToken, + }, + headers: { + 'Content-Type': 'application/json', + }, + body: requestBody, + }); - async searchDeals(accessToken: string, filters?: { - createdAt?: number, - createdAtOperator?: string - }) { - const searchParams = []; + return response.body; + }, - if (filters && filters.createdAt) { - searchParams.push({ - propertyName: 'createdate', - operator: filters.createdAtOperator ?? 'GT', - value: filters.createdAt - }) - } + async searchDeals( + accessToken: string, + filters?: { + createdAt?: number; + createdAtOperator?: string; + } + ) { + const searchParams = []; - const response = await httpClient.sendRequest({ - method: HttpMethod.POST, - url: `${API}/crm/v3/objects/deals/search`, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: accessToken, - }, - headers: { - "Content-Type": 'application/json' - }, - body: { - filters: searchParams - } - }); + if (filters && filters.createdAt) { + searchParams.push({ + propertyName: 'createdate', + operator: filters.createdAtOperator ?? 'GT', + value: filters.createdAt, + }); + } - return response.body; - }, + const response = await httpClient.sendRequest({ + method: HttpMethod.POST, + url: `${API}/crm/v3/objects/deals/search`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: accessToken, + }, + headers: { + 'Content-Type': 'application/json', + }, + body: { + filters: searchParams, + }, + }); - async searchTickets(accessToken: string, filters?: { - createdAt?: number, - createdAtOperator?: string - }) { - const searchParams = []; + return response.body; + }, - if (filters && filters.createdAt) { - searchParams.push({ - propertyName: 'createdate', - operator: filters.createdAtOperator ?? 'GT', - value: filters.createdAt - }) - } + async searchTickets( + accessToken: string, + filters?: { + createdAt?: number; + createdAtOperator?: string; + } + ) { + const searchParams = []; - const response = await httpClient.sendRequest({ - method: HttpMethod.POST, - url: `${API}/crm/v3/objects/tickets/search`, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: accessToken, - }, - headers: { - "Content-Type": 'application/json' - }, - body: { - filters: searchParams - } - }); + if (filters && filters.createdAt) { + searchParams.push({ + propertyName: 'createdate', + operator: filters.createdAtOperator ?? 'GT', + value: filters.createdAt, + }); + } - return response.body; - }, + const response = await httpClient.sendRequest({ + method: HttpMethod.POST, + url: `${API}/crm/v3/objects/tickets/search`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: accessToken, + }, + headers: { + 'Content-Type': 'application/json', + }, + body: { + filters: searchParams, + }, + }); + + return response.body; + }, + async listContactOwners(accessToken: string, email?: string) { + const response = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${API}/crm/v3/owners`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: accessToken, + }, + headers: { + 'Content-Type': 'application/json', + }, + queryParams: { + email: email!, + }, + }); + return response.body; + }, }; type ContactsCreateOrUpdateParams = { - token: string; - email: string; - contact: Partial; + token: string; + email: string; + contact: Partial; }; type GetStaticListsParams = { - token: string; -} + token: string; +}; type ListsAddContactParams = { - token: string; - listId: number; - email: string; -} + token: string; + listId: number; + email: string; +}; diff --git a/packages/pieces/hubspot/src/lib/common/index.ts b/packages/pieces/hubspot/src/lib/common/index.ts index a6705f2fb..4f99b071c 100644 --- a/packages/pieces/hubspot/src/lib/common/index.ts +++ b/packages/pieces/hubspot/src/lib/common/index.ts @@ -1,178 +1,227 @@ -import { AuthenticationType, HttpMethod, HttpRequest, httpClient } from "@activepieces/pieces-common"; -import { CheckboxProperty, DateTimeProperty, FileProperty, LongTextProperty, NumberProperty, OAuth2PropertyValue, Property, ShortTextProperty } from "@activepieces/pieces-framework"; +import { + AuthenticationType, + HttpMethod, + HttpRequest, + httpClient, +} from '@activepieces/pieces-common'; +import { + CheckboxProperty, + DateTimeProperty, + FileProperty, + LongTextProperty, + NumberProperty, + OAuth2PropertyValue, + Property, + ShortTextProperty, +} from '@activepieces/pieces-framework'; +import { hubSpotClient } from './client'; enum HubspotFieldType { - BooleanCheckBox = "booleancheckbox", - Date = "date", - File = "file", - Number = "number", - CalculationEquation = "calculation_equation", - PhoneNumber = "phonenumber", - Text = "text", - TextArea = "textarea", - Html = "html", - CheckBox = "checkbox", - Select = "select", - Radio = "radio", + BooleanCheckBox = 'booleancheckbox', + Date = 'date', + File = 'file', + Number = 'number', + CalculationEquation = 'calculation_equation', + PhoneNumber = 'phonenumber', + Text = 'text', + TextArea = 'textarea', + Html = 'html', + CheckBox = 'checkbox', + Select = 'select', + Radio = 'radio', } interface ContactProperty { - name: string; - label: string; - description: string; - type: string; - fieldType: HubspotFieldType; - options: []; -} + name: string; + label: string; + description: string; + type: string; + fieldType: HubspotFieldType; + options: []; +} -type DynamicPropsValue = ShortTextProperty | LongTextProperty | CheckboxProperty | DateTimeProperty | FileProperty | NumberProperty +type DynamicPropsValue = + | ShortTextProperty + | LongTextProperty + | CheckboxProperty + | DateTimeProperty + | FileProperty + | NumberProperty; export const hubspotCommon = { - choose_props : Property.MultiSelectDropdown({ - displayName: 'Properties', - description: 'Choose extra properties to add to the contact', - required: false, - refreshers: [ 'auth' ], - options: async ({ auth }) => { - const connection = auth as OAuth2PropertyValue; - if( !connection ){ - return { - disabled: true, - options: [], - placeholder: 'please authenticate your account first before selecting properties', - } - } - try{ - const request: HttpRequest = { - method: HttpMethod.GET, - url: 'https://api.hubapi.com/properties/v1/contacts/properties', - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: connection.access_token, - } - } - const result = await httpClient.sendRequest(request); - - const properties = result.body.map((property: any) => { - return { - label: property.label, - value: property, - } - }); - - return { - disabled: false, - options: properties, - } - } - catch( error ){ - return { - disabled: true, - options: [], - placeholder: 'An error occurred while fetching properties', - } - } - } - }), - dynamicProperties : Property.DynamicProperties({ - displayName: 'Dynamic Properties', - description: 'Extra properties to add to the contact', - required: false, - refreshers: [ 'choose_props' ], - props: async ( {choose_props} ) => { + choose_props: Property.MultiSelectDropdown({ + displayName: 'Properties', + description: 'Choose extra properties to add to the contact', + required: false, + refreshers: ['auth'], + options: async ({ auth }) => { + const connection = auth as OAuth2PropertyValue; + if (!connection) { + return { + disabled: true, + options: [], + placeholder: + 'please authenticate your account first before selecting properties', + }; + } + try { + const request: HttpRequest = { + method: HttpMethod.GET, + url: 'https://api.hubapi.com/properties/v1/contacts/properties', + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: connection.access_token, + }, + }; + const result = await httpClient.sendRequest(request); - const all_props = choose_props as ContactProperty[]; - - if( !all_props ){ - return {}; - } + const properties = result.body.map((property: any) => { + return { + label: property.label, + value: property, + }; + }); - const fields: any = {}; + return { + disabled: false, + options: properties, + }; + } catch (error) { + return { + disabled: true, + options: [], + placeholder: 'An error occurred while fetching properties', + }; + } + }, + }), + dynamicProperties: Property.DynamicProperties({ + displayName: 'Dynamic Properties', + description: 'Extra properties to add to the contact', + required: false, + refreshers: ['choose_props'], + props: async ({ auth, choose_props }) => { + const all_props = choose_props as ContactProperty[]; - for( const prop of all_props ){ - switch( prop.fieldType ){ - case HubspotFieldType.BooleanCheckBox: - fields[prop.name] = Property.Checkbox({ - displayName: prop.label, - description: prop.description, - required: false, - }) - break; - case HubspotFieldType.Date: - fields[prop.name] = Property.DateTime({ - displayName: prop.label, - description: prop.description, - required: false, - }) - break; - case HubspotFieldType.File: - fields[prop.name] = Property.File({ - displayName: prop.label, - description: prop.description, - required: false, - }) - break; - case HubspotFieldType.Number: - fields[prop.name] = Property.Number({ - displayName: prop.label, - description: prop.description, - required: false, - }) - break; - case HubspotFieldType.CalculationEquation: - case HubspotFieldType.PhoneNumber: - case HubspotFieldType.Text: - fields[prop.name] = Property.ShortText({ - displayName: prop.label, - description: prop.description, - required: false, - }) - break; - case HubspotFieldType.TextArea: - case HubspotFieldType.Html: - fields[prop.name] = Property.LongText({ - displayName: prop.label, - description: prop.description, - required: false, - }) - break; - case HubspotFieldType.CheckBox: - fields[prop.name] = Property.StaticMultiSelectDropdown({ - displayName: prop.label, - description: prop.description, - required: false, - options: { - options: prop.options.map((option: any) => { - return { - label: option.label, - value: option.value, - } - }) - } - }) - break; - case HubspotFieldType.Select: - case HubspotFieldType.Radio: - fields[prop.name] = Property.StaticDropdown({ - displayName: prop.label, - description: prop.description, - required: false, - options: { - options: prop.options.map((option: any) => { - return { - label: option.label, - value: option.value, - } - }) - } - }) - break; + if (!all_props) { + return {}; + } - } - } + const fields: any = {}; - return fields; + for (const prop of all_props) { + switch (prop.fieldType) { + case HubspotFieldType.BooleanCheckBox: + fields[prop.name] = Property.Checkbox({ + displayName: prop.label, + description: prop.description, + required: false, + }); + break; + case HubspotFieldType.Date: + fields[prop.name] = Property.DateTime({ + displayName: prop.label, + description: prop.description, + required: false, + }); + break; + case HubspotFieldType.File: + fields[prop.name] = Property.File({ + displayName: prop.label, + description: prop.description, + required: false, + }); + break; + case HubspotFieldType.Number: + fields[prop.name] = Property.Number({ + displayName: prop.label, + description: prop.description, + required: false, + }); + break; + case HubspotFieldType.CalculationEquation: + case HubspotFieldType.PhoneNumber: + case HubspotFieldType.Text: + fields[prop.name] = Property.ShortText({ + displayName: prop.label, + description: prop.description, + required: false, + }); + break; + case HubspotFieldType.TextArea: + case HubspotFieldType.Html: + fields[prop.name] = Property.LongText({ + displayName: prop.label, + description: prop.description, + required: false, + }); + break; + case HubspotFieldType.CheckBox: + fields[prop.name] = Property.StaticMultiSelectDropdown({ + displayName: prop.label, + description: prop.description, + required: false, + options: { + options: prop.options.map((option: any) => { + return { + label: option.label, + value: option.value, + }; + }), + }, + }); + break; + case HubspotFieldType.Select: + case HubspotFieldType.Radio: + if (prop.name === 'hubspot_owner_id') { + try { + const res = + ( + await hubSpotClient.listContactOwners( + auth.access_token as string + ) + ).results ?? []; + fields[prop.name] = Property.StaticDropdown({ + displayName: prop.label, + description: prop.description, + required: false, + options: { + options: res.map((owner: { id: string; email: string }) => { + return { + label: owner.email, + value: owner.id, + }; + }), + }, + }); + } catch (error) { + return { + disabled: true, + options: [], + placeholder: + 'An error occurred while fetching contact owner list.', + }; + } + } else { + fields[prop.name] = Property.StaticDropdown({ + displayName: prop.label, + description: prop.description, + required: false, + options: { + options: prop.options.map((option: any) => { + return { + label: option.label, + value: option.value, + }; + }), + }, + }); + } + break; } - }) -} + } + return fields; + }, + }), +}; diff --git a/packages/pieces/instagram-business/package.json b/packages/pieces/instagram-business/package.json index aad2b5d3b..54e271842 100644 --- a/packages/pieces/instagram-business/package.json +++ b/packages/pieces/instagram-business/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-instagram-business", - "version": "0.1.0" + "version": "0.1.1" } diff --git a/packages/pieces/instagram-business/src/lib/common/index.ts b/packages/pieces/instagram-business/src/lib/common/index.ts index 6cf57f6af..e0634cdc0 100644 --- a/packages/pieces/instagram-business/src/lib/common/index.ts +++ b/packages/pieces/instagram-business/src/lib/common/index.ts @@ -18,7 +18,7 @@ export const instagramCommon = { authUrl: "https://graph.facebook.com/oauth/authorize", tokenUrl: "https://graph.facebook.com/oauth/access_token", required: true, - scope: ['instagram_basic', 'instagram_content_publish', 'business_management', 'ads_management', 'pages_read_engagement'], + scope: ['instagram_basic', 'instagram_content_publish', 'pages_show_list'], }), page: Property.Dropdown({ diff --git a/packages/pieces/instant-form/package.json b/packages/pieces/instant-form/package.json index b5a512180..b2b9da28a 100644 --- a/packages/pieces/instant-form/package.json +++ b/packages/pieces/instant-form/package.json @@ -1,4 +1,4 @@ { "name": "@tookey-io/piece-instant-form", - "version": "0.0.2" + "version": "0.0.3" } diff --git a/packages/pieces/instant-form/src/index.ts b/packages/pieces/instant-form/src/index.ts index 3072c99ec..b9b4fda1f 100644 --- a/packages/pieces/instant-form/src/index.ts +++ b/packages/pieces/instant-form/src/index.ts @@ -75,7 +75,6 @@ export const instantFormNewResponse = createTrigger({ }) }, auth: InstantFormAuth, - requireAuth: true, type: TriggerStrategy.WEBHOOK, async onEnable(ctx) { const id = await httpClient.sendRequest<{ data: {id: string } }>({ diff --git a/packages/pieces/linkedin/package.json b/packages/pieces/linkedin/package.json index bf6a393bf..3b10754b6 100644 --- a/packages/pieces/linkedin/package.json +++ b/packages/pieces/linkedin/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-linkedin", - "version": "0.1.2" + "version": "0.1.3" } diff --git a/packages/pieces/linkedin/src/lib/actions/create-share-update.ts b/packages/pieces/linkedin/src/lib/actions/create-share-update.ts index 682ba3131..ba8ce170b 100644 --- a/packages/pieces/linkedin/src/lib/actions/create-share-update.ts +++ b/packages/pieces/linkedin/src/lib/actions/create-share-update.ts @@ -38,7 +38,7 @@ export const createShareUpdate = createAction({ image }); const createPostHeaders: any = linkedinCommon.linkedinHeaders - createPostHeaders["LinkedIn-Version"] = '202306'; + createPostHeaders["LinkedIn-Version"] = '202312'; const request: HttpRequest = { method: HttpMethod.POST, diff --git a/packages/pieces/linkedin/src/lib/common/index.ts b/packages/pieces/linkedin/src/lib/common/index.ts index e489f101a..c7b07fda4 100644 --- a/packages/pieces/linkedin/src/lib/common/index.ts +++ b/packages/pieces/linkedin/src/lib/common/index.ts @@ -1,8 +1,16 @@ -import { ApFile, Property } from "@activepieces/pieces-framework"; +import { ApFile, Property, ProcessorFn} from "@activepieces/pieces-framework"; import { HttpMethod, httpClient, AuthenticationType } from "@activepieces/pieces-common"; import FormData from 'form-data'; +const processText: ProcessorFn = (property, text) => { + // LinkedIn Posts API has a list of characters that need to be escaped since it's type is "LittleText" + // https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api?view=li-lms-2023-11&tabs=http + // https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/little-text-format?view=li-lms-2023-11 + // eslint-disable-next-line no-useless-escape + return text.replace(/[\(*\)\[\]\{\}<>@|~_]/gm, (x:string) => "\\" + x); +} + export const linkedinCommon = { baseUrl: 'https://api.linkedin.com', linkedinHeaders: { @@ -10,6 +18,7 @@ export const linkedinCommon = { }, text: Property.LongText({ displayName: 'Text', + processors: [processText], required: true }), imageUrl: Property.File({ @@ -109,7 +118,7 @@ export const linkedinCommon = { generatePostRequestBody: (data: { urn: string, - text: string, + text: any, link?: string | undefined, linkTitle?: string | undefined, linkDescription?: string | undefined, diff --git a/packages/pieces/monday/package.json b/packages/pieces/monday/package.json index ede6fbafd..c25dc97af 100644 --- a/packages/pieces/monday/package.json +++ b/packages/pieces/monday/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-monday", - "version": "0.1.5" + "version": "0.2.5" } \ No newline at end of file diff --git a/packages/pieces/monday/src/index.ts b/packages/pieces/monday/src/index.ts index 6119b1654..cad5c046e 100644 --- a/packages/pieces/monday/src/index.ts +++ b/packages/pieces/monday/src/index.ts @@ -1,34 +1,43 @@ -import { PieceAuth, createPiece } from "@activepieces/pieces-framework"; -import { mondayCreateAnItem } from "./lib/actions/create-item"; -import { mondayUpdateAnItem } from "./lib/actions/update-item"; -import { mondayGetItemColumnValues } from "./lib/actions/get-column-value-by-item"; -import { mondayItemCreatedTrigger } from "./lib/triggers/item-created-trigger"; -import { mondayNewUpdatesTrigger } from "./lib/triggers/new-update-trigger"; -import { mondayGetItemByColumnValues } from './lib/actions/get-item-by-column-value'; +import { PieceAuth, createPiece } from '@activepieces/pieces-framework'; +import { createColumnAction } from './lib/actions/create-column'; +import { createGroupAction } from './lib/actions/create-group'; +import { createItemAction } from './lib/actions/create-item'; +import { createUpdateAction } from './lib/actions/create-update'; +import { getBoardItemValuesAction } from './lib/actions/get-board-values'; +import { getItemsColumnValuesAction } from './lib/actions/get-column-values'; +import { updateColumnValuesOfItemAction } from './lib/actions/update-column-values-of-item'; +import { updateItemNameAction } from './lib/actions/update-item-name'; +import { newItemInBoardTrigger } from './lib/triggers/new-item-in-board'; +import { specificColumnValueUpdatedTrigger } from './lib/triggers/specific-column-updated'; -export const mondayAuth = PieceAuth.OAuth2({ - - description: "OAuth2.0 Authentication", - authUrl: "https://auth.monday.com/oauth2/authorize", - tokenUrl: "https://auth.monday.com/oauth2/token", +const markdown = ` +1.Log into your monday.com account.\n +2.Click on your avatar/profile picture in the top right corner.\n +3.Select **Administration** (this requires you to have admin permissions).\n +4.Go to the **API** section.\n +5.Copy your personal token`; + +export const mondayAuth = PieceAuth.SecretText({ + displayName: 'API v2 Token', + description: markdown, required: true, - scope: [ - 'workspaces:read', - 'boards:read', - 'boards:write', - 'updates:read', - 'updates:write', - 'webhooks:write', - 'webhooks:read' - ] -}) +}); export const monday = createPiece({ - displayName: "monday.com", - minimumSupportedRelease: '0.5.0', - logoUrl: "https://cdn.activepieces.com/pieces/monday.png", - authors: ['kanarelo'], + displayName: 'monday.com', + minimumSupportedRelease: '0.5.0', + logoUrl: 'https://cdn.activepieces.com/pieces/monday.png', + authors: ['kanarelo', 'kishanprmr'], auth: mondayAuth, - actions: [mondayCreateAnItem, mondayUpdateAnItem, mondayGetItemColumnValues, mondayGetItemByColumnValues], - triggers: [mondayItemCreatedTrigger, mondayNewUpdatesTrigger], + actions: [ + createColumnAction, + createGroupAction, + createItemAction, + createUpdateAction, + getBoardItemValuesAction, + getItemsColumnValuesAction, + updateColumnValuesOfItemAction, + updateItemNameAction, + ], + triggers: [newItemInBoardTrigger, specificColumnValueUpdatedTrigger], }); diff --git a/packages/pieces/monday/src/lib/actions/create-column.ts b/packages/pieces/monday/src/lib/actions/create-column.ts new file mode 100644 index 000000000..16cc0100b --- /dev/null +++ b/packages/pieces/monday/src/lib/actions/create-column.ts @@ -0,0 +1,37 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; +import { COLUMN_TYPE_OPTIONS } from '../common/constants'; + +export const createColumnAction = createAction({ + auth: mondayAuth, + name: 'monday_create_column', + displayName: 'Create Column', + description: 'Creates a new column in board.', + props: { + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + column_title: Property.ShortText({ + displayName: 'Column Title', + required: true, + }), + column_type: Property.StaticDropdown({ + displayName: 'Column Type', + required: true, + options: { + disabled: false, + options: COLUMN_TYPE_OPTIONS, + }, + }), + }, + async run(context) { + const { board_id, column_title, column_type } = context.propsValue; + + const client = makeClient(context.auth as string); + return await client.createColumn({ + boardId: board_id as string, + columnTitle: column_title as string, + columnType: column_type as string, + }); + }, +}); diff --git a/packages/pieces/monday/src/lib/actions/create-group.ts b/packages/pieces/monday/src/lib/actions/create-group.ts new file mode 100644 index 000000000..d08c607b2 --- /dev/null +++ b/packages/pieces/monday/src/lib/actions/create-group.ts @@ -0,0 +1,27 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; + +export const createGroupAction = createAction({ + auth: mondayAuth, + name: 'monday_create_group', + displayName: 'Create Group', + description: 'Creates a new group in board.', + props: { + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + group_name: Property.ShortText({ + displayName: 'Group Name', + required: true, + }), + }, + async run(context) { + const { board_id, group_name } = context.propsValue; + + const client = makeClient(context.auth as string); + return await client.createGroup({ + boardId: board_id as string, + groupName: group_name as string, + }); + }, +}); diff --git a/packages/pieces/monday/src/lib/actions/create-item.ts b/packages/pieces/monday/src/lib/actions/create-item.ts index ed2362bfa..943c9a92a 100644 --- a/packages/pieces/monday/src/lib/actions/create-item.ts +++ b/packages/pieces/monday/src/lib/actions/create-item.ts @@ -1,69 +1,70 @@ -import { HttpMethod } from "@activepieces/pieces-common" -import { createAction, Property } from "@activepieces/pieces-framework" -import { mondayProps } from "../common/props" -import { mondayMakeRequest } from "../common/data" -import { mondayAuth } from "../.." +import { + DynamicPropsValue, + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; +import { + convertPropValueToMondayColumnValue, + generateColumnIdTypeMap, +} from '../common/helper'; -export const mondayCreateAnItem = createAction({ +export const createItemAction = createAction({ auth: mondayAuth, - name: 'monday_create_an_item', + name: 'monday_create_item', displayName: 'Create Item', - description: 'Create a new item inside a board.', + description: 'Creates a new item inside a board.', props: { - workspace_id: mondayProps.workspace_id(true), - board_id: mondayProps.board_id(true), - group_id: mondayProps.group_id(false), + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + group_id: mondayCommon.group_id(false), item_name: Property.ShortText({ - displayName: "Item Name", - description: "Item Name", - required: true - }), - column_values: Property.Object({ - displayName: "Column Values", - description: "The column values of the new item.", - required: false + displayName: 'Item Name', + description: 'Item Name', + required: true, }), + column_values: mondayCommon.columnValues, create_labels_if_missing: Property.Checkbox({ - displayName: "Create Labels if Missing", - description: "Creates status/dropdown labels if they are missing. This requires permission to change the board structure.", + displayName: 'Create Labels if Missing', + description: + 'Creates status/dropdown labels if they are missing. This requires permission to change the board structure.', defaultValue: false, - required: false - }) + required: false, + }), }, async run(context) { - const { ...itemValues } = context.propsValue + const { board_id, item_name, create_labels_if_missing } = + context.propsValue; + const group_id = context.propsValue.group_id!; + const columnValuesInput = context.propsValue.column_values; + const mondayColumnValues: DynamicPropsValue = {}; + const client = makeClient(context.auth as string); + const res = await client.listBoardColumns({ + boardId: board_id as unknown as string, + }); + const columns = res.data.boards[0]?.columns; - const query = ` - mutation { - create_item ( - item_name: "${itemValues.item_name}", - board_id: ${itemValues.board_id}, - ${itemValues.group_id ? `group_id: ${itemValues.group_id},` : ``} - create_labels_if_missing: ${ - itemValues.create_labels_if_missing ?? false - }, - ${ - itemValues.column_values - ? `column_values: " ${JSON.stringify( - itemValues?.column_values - ).replace(/"/g, '\\"')}",` - : `` - } - ) - { id } - } - ` + // map board column id with column type + const columnIdTypeMap = generateColumnIdTypeMap(columns); - const result = await mondayMakeRequest( - context.auth.access_token, - query, - HttpMethod.POST - ) + Object.keys(columnValuesInput).forEach((key) => { + if (columnValuesInput[key] !== '') { + const columnType: string = columnIdTypeMap[key]; + mondayColumnValues[key] = convertPropValueToMondayColumnValue( + columnType, + columnValuesInput[key] + ); + } + }); - if (result.status === 200) { - return result.body - } - return result - } -}) + return await client.createItem({ + itemName: item_name, + boardId: board_id, + groupId: group_id, + columnValues: JSON.stringify(mondayColumnValues), + craeteLables: create_labels_if_missing ?? false, + }); + }, +}); diff --git a/packages/pieces/monday/src/lib/actions/create-update.ts b/packages/pieces/monday/src/lib/actions/create-update.ts new file mode 100644 index 000000000..969294ff2 --- /dev/null +++ b/packages/pieces/monday/src/lib/actions/create-update.ts @@ -0,0 +1,29 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient } from '../common'; + +export const createUpdateAction = createAction({ + auth: mondayAuth, + name: 'monday_create_update', + displayName: 'Create Update', + description: 'Creates a new update.', + props: { + item_id: Property.ShortText({ + displayName: 'Item ID', + required: true, + }), + body: Property.LongText({ + displayName: 'Body', + required: true, + }), + }, + async run(context) { + const { item_id, body } = context.propsValue; + + const client = makeClient(context.auth as string); + return await client.createUpdate({ + itemId: item_id as string, + body: body as string, + }); + }, +}); diff --git a/packages/pieces/monday/src/lib/actions/get-board-values.ts b/packages/pieces/monday/src/lib/actions/get-board-values.ts new file mode 100644 index 000000000..65e497e44 --- /dev/null +++ b/packages/pieces/monday/src/lib/actions/get-board-values.ts @@ -0,0 +1,40 @@ +import { createAction } from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; +import { parseMondayColumnValue } from '../common/helper'; + +export const getBoardItemValuesAction = createAction({ + auth: mondayAuth, + name: 'monday_get_board_values', + displayName: 'Get Board Values', + description: "Gets a list of board's items.", + props: { + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + column_ids: mondayCommon.columnIds(false), + }, + async run(context) { + const { board_id, column_ids } = context.propsValue; + + const client = makeClient(context.auth as string); + const res = await client.getBoardItemValues({ + boardId: board_id as string, + columnIds: column_ids as string[], + }); + const items = res.data.boards[0].items_page.items; + + const result = []; + for (const item of items) { + const transformedValues: Record = { + id: item.id, + name: item.name, + }; + for (const column of item.column_values) { + transformedValues[column.id] = parseMondayColumnValue(column); + } + result.push(transformedValues); + } + + return result; + }, +}); diff --git a/packages/pieces/monday/src/lib/actions/get-column-value-by-item.ts b/packages/pieces/monday/src/lib/actions/get-column-value-by-item.ts deleted file mode 100644 index bc8cd3794..000000000 --- a/packages/pieces/monday/src/lib/actions/get-column-value-by-item.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { HttpMethod } from "@activepieces/pieces-common" -import { Property, createAction } from "@activepieces/pieces-framework" -import { mondayProps } from "../common/props" -import { mondayMakeRequest } from "../common/data" -import { mondayAuth } from "../.." - -export const mondayGetItemColumnValues = createAction({ - auth: mondayAuth, - name: 'monday_get_item-col-val', - displayName: 'Get Column Values', - description: 'Get Column values by providing item id.', - props: { - workspace_id: mondayProps.workspace_id(true), - board_id: mondayProps.board_id(true), - item_id: Property.ShortText({ - displayName: 'Item Id', - description: "Value of an item's id", - required: true, - }), - }, - async run(context) { - const { ...itemValues } = context.propsValue - - const query = ` - query { - items ( - ids: ${[itemValues.item_id]}, - ) - { - id - name - column_values { - id - value - text - } - } - } - ` - - const result = await mondayMakeRequest( - context.auth.access_token, - query, - HttpMethod.GET - ) - - if (result.status === 200) { - return result.body?.data?.items?.[0] - } - return result - } -}) diff --git a/packages/pieces/monday/src/lib/actions/get-column-values.ts b/packages/pieces/monday/src/lib/actions/get-column-values.ts new file mode 100644 index 000000000..5b2da8e3b --- /dev/null +++ b/packages/pieces/monday/src/lib/actions/get-column-values.ts @@ -0,0 +1,36 @@ +import { createAction } from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; +import { parseMondayColumnValue } from '../common/helper'; + +export const getItemsColumnValuesAction = createAction({ + auth: mondayAuth, + name: 'monday_get_item_column_values', + displayName: "Get an Item's Column Values", + description: 'Gets column values of an item.', + props: { + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + item_id: mondayCommon.item_id(true), + column_ids: mondayCommon.columnIds(false), + }, + async run(context) { + const { board_id, item_id, column_ids } = context.propsValue; + + const client = makeClient(context.auth as string); + const res = await client.getItemColumnValues({ + boardId: board_id as string, + itemId: item_id as string, + columnIds: column_ids as string[], + }); + const item = res.data.boards[0].items_page.items[0]; + const transformedValues: Record = { + id: item.id, + name: item.name, + }; + for (const column of item.column_values) { + transformedValues[column.id] = parseMondayColumnValue(column); + } + return transformedValues; + }, +}); diff --git a/packages/pieces/monday/src/lib/actions/get-item-by-column-value.ts b/packages/pieces/monday/src/lib/actions/get-item-by-column-value.ts deleted file mode 100644 index a73e6c294..000000000 --- a/packages/pieces/monday/src/lib/actions/get-item-by-column-value.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { HttpMethod } from '@activepieces/pieces-common'; -import { Property, createAction } from '@activepieces/pieces-framework'; -import { mondayProps } from '../common/props'; -import { mondayMakeRequest } from '../common/data'; -import { mondayAuth } from '../..'; - -export const mondayGetItemByColumnValues = createAction({ - auth: mondayAuth, - name: 'monday_get_item-by-col-val', - displayName: 'Get Item by Column Values', - description: 'Get item by providing column value.', - props: { - workspace_id: mondayProps.workspace_id(true), - board_id: mondayProps.board_id(true), - column_id: Property.ShortText({ - displayName: 'Column Id', - description: "Value of an item's column", - required: true, - }), - column_values: Property.Array({ - displayName: 'Column Values', - description: 'Column values to search', - required: true, - }), - }, - async run(context) { - const { ...itemValues } = context.propsValue; - - const item = itemValues.column_values?.map((value) => `"${value}"`); - - const query = ` - query { - items_by_column_values( - board_id: ${itemValues.board_id}, - column_id: "${itemValues.column_id}", - column_value: ${item} - ) { - id, - name - } - } - `; - - const result = await mondayMakeRequest( - context.auth.access_token, - query, - HttpMethod.GET - ); - - if (result.status === 200) { - return result.body?.data?.items_by_column_values; - } - return result; - }, -}); diff --git a/packages/pieces/monday/src/lib/actions/update-column-values-of-item.ts b/packages/pieces/monday/src/lib/actions/update-column-values-of-item.ts new file mode 100644 index 000000000..e3801cd34 --- /dev/null +++ b/packages/pieces/monday/src/lib/actions/update-column-values-of-item.ts @@ -0,0 +1,53 @@ +import { + DynamicPropsValue, + createAction, +} from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; +import { + convertPropValueToMondayColumnValue, + generateColumnIdTypeMap, +} from '../common/helper'; + +export const updateColumnValuesOfItemAction = createAction({ + auth: mondayAuth, + name: 'monday_update_column_values_of_item', + displayName: 'Update Column Values of Specific Item', + description: 'Updates multiple columns values of specific item.', + props: { + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + item_id: mondayCommon.item_id(true), + column_values: mondayCommon.columnValues, + }, + async run(context) { + const { board_id, item_id } = context.propsValue; + const columnValuesInput = context.propsValue.column_values; + const mondayColumnValues: DynamicPropsValue = {}; + + const client = makeClient(context.auth as string); + const res = await client.listBoardColumns({ + boardId: board_id as unknown as string, + }); + const columns = res.data.boards[0]?.columns; + + // map board column id with column type + const columnIdTypeMap = generateColumnIdTypeMap(columns); + + Object.keys(columnValuesInput).forEach((key) => { + if (columnValuesInput[key] !== '') { + const columnType: string = columnIdTypeMap[key]; + mondayColumnValues[key] = convertPropValueToMondayColumnValue( + columnType, + columnValuesInput[key] + ); + } + }); + + return await client.updateItem({ + boardId: board_id, + itemId: item_id, + columnValues: JSON.stringify(mondayColumnValues), + }); + }, +}); diff --git a/packages/pieces/monday/src/lib/actions/update-item-name.ts b/packages/pieces/monday/src/lib/actions/update-item-name.ts new file mode 100644 index 000000000..7ad9a50b1 --- /dev/null +++ b/packages/pieces/monday/src/lib/actions/update-item-name.ts @@ -0,0 +1,29 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; + +export const updateItemNameAction = createAction({ + auth: mondayAuth, + name: 'monday_update_item_name', + displayName: 'Update Item Name', + description: 'Updates an item name.', + props: { + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + item_id: mondayCommon.item_id(true), + name: Property.ShortText({ + displayName: 'New Item name', + required: true, + }), + }, + async run(context) { + const { board_id, item_id, name } = context.propsValue; + + const client = makeClient(context.auth as string); + return await client.updateItem({ + boardId: board_id, + itemId: item_id, + columnValues: JSON.stringify({ name: name }), + }); + }, +}); diff --git a/packages/pieces/monday/src/lib/actions/update-item.ts b/packages/pieces/monday/src/lib/actions/update-item.ts deleted file mode 100644 index ac392d7d9..000000000 --- a/packages/pieces/monday/src/lib/actions/update-item.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { HttpMethod } from "@activepieces/pieces-common" -import { createAction, Property } from "@activepieces/pieces-framework" -import { mondayProps } from "../common/props" -import { mondayMakeRequest } from "../common/data" -import { mondayAuth } from "../.." - -export const mondayUpdateAnItem = createAction({ - auth: mondayAuth, - name: 'monday_update_an_item', - displayName: 'Update Item', - description: 'Update an item inside a board.', - props: { - workspace_id: mondayProps.workspace_id(true), - board_id: mondayProps.board_id(true), - item_id: mondayProps.item_id(true), - column_values: Property.Object({ - displayName: "Column Values", - description: "The column values that you need to update.", - required: false - }), - create_labels_if_missing: Property.Checkbox({ - displayName: "Create Labels if Missing", - description: "Creates status/dropdown labels if they are missing. This requires permission to change the board structure.", - defaultValue: false, - required: false - }) - }, - async run(context) { - const { ...itemValues } = context.propsValue; - const query = `mutation{change_multiple_column_values (item_id: ${ - itemValues.item_id - } - ,board_id: ${itemValues.board_id} - ,create_labels_if_missing: ${ - itemValues.create_labels_if_missing ?? true - } - ,column_values: " ${JSON.stringify(itemValues?.column_values).replace( - /"/g, - '\\"' - )}"){id}}`; - - const result = await mondayMakeRequest( - context.auth.access_token, - query, - HttpMethod.POST - ); - - if (result.status === 200) { - return result.body - } - return result - } -}) diff --git a/packages/pieces/monday/src/lib/common/client.ts b/packages/pieces/monday/src/lib/common/client.ts new file mode 100644 index 000000000..5078d97b2 --- /dev/null +++ b/packages/pieces/monday/src/lib/common/client.ts @@ -0,0 +1,101 @@ +import mondaySdk from 'monday-sdk-js'; +import { MondayClientSdk } from 'monday-sdk-js/types/client-sdk.interface'; +import { Board, MondayColumn, User } from './models'; +import { mondayGraphQLMutations } from './mutations'; +import { mondayGraphQLQueries } from './queries'; +export class mondayClient { + private client: MondayClientSdk; + constructor(apiKey: string) { + this.client = mondaySdk(); + this.client.setToken(apiKey); + this.client.setApiVersion('2023-10'); + } + async listWorkspcaes() { + return await this.client.api<{ + workspaces: { id: string; name: string }[]; + }>(mondayGraphQLQueries.listWorkspaces); + } + async listWorkspaceBoards(variables: object) { + return await this.client.api<{ + boards: { id: string; name: string; type: string }[]; + }>(mondayGraphQLQueries.listWorkspaceBoards, { variables: variables }); + } + async listBoardGroups(variables: object) { + return await this.client.api<{ boards: Board[] }>( + mondayGraphQLQueries.listBoardGroups, + { variables: variables } + ); + } + async listBoardColumns(variables: object) { + return await this.client.api<{ boards: { columns: MondayColumn[] }[] }>( + mondayGraphQLQueries.listBoardColumns, + { variables: variables } + ); + } + async listBoardItems(variables: object) { + return await this.client.api<{ boards: Board[] }>( + mondayGraphQLQueries.listBoardItems, + { variables: variables } + ); + } + async createItem(variables: object) { + return await this.client.api(mondayGraphQLMutations.createItem, { + variables: variables, + }); + } + async updateItem(variables: object) { + return await this.client.api(mondayGraphQLMutations.updateItem, { + variables: variables, + }); + } + async createWebhook(variables: object) { + return await this.client.api<{ id: string; board_id: string }>( + mondayGraphQLMutations.createWebhook, + { + variables: variables, + } + ); + } + async deleteWebhook(variables: object) { + return await this.client.api(mondayGraphQLMutations.deleteWebhook, { + variables: variables, + }); + } + async listUsers() { + return await this.client.api<{ users: User[] }>( + mondayGraphQLQueries.listUsers + ); + } + async getBoardItemValues(variables: object) { + return await this.client.api<{ boards: Board[] }>( + mondayGraphQLQueries.getBoardItemValues, + { variables: variables } + ); + } + async getItemColumnValues(variables: object) { + return await this.client.api<{ boards: Board[] }>( + mondayGraphQLQueries.getItemColumnValues, + { + variables: variables, + } + ); + } + async createColumn(variables: object) { + return await this.client.api<{ id: string }>( + mondayGraphQLMutations.createColumn, + { variables: variables } + ); + } + async createGroup(variables: object) { + return await this.client.api<{ id: string }>( + mondayGraphQLMutations.createGroup, + { variables: variables } + ); + } + async createUpdate(variables: object) { + return await this.client.api<{ id: string }>( + mondayGraphQLMutations.createUpdate, + { variables: variables } + ); + } +} diff --git a/packages/pieces/monday/src/lib/common/constants.ts b/packages/pieces/monday/src/lib/common/constants.ts new file mode 100644 index 000000000..04417754a --- /dev/null +++ b/packages/pieces/monday/src/lib/common/constants.ts @@ -0,0 +1,199 @@ +export const enum MondayColumnType { + AUTO_NUMBER = 'auto_number', + BOARD_RELATION = 'board_relation', + BUTTON = 'button', + CHECKBOX = 'checkbox', + COLOR_PICKER = 'color_picker', + COUNTRY = 'country', + CREATION_LOG = 'creation_log', + DATE = 'date', + DEPENDENCY = 'dependency', + DOC = 'doc', + DROPDOWN = 'dropdown', + EMAIL = 'email', + FILE = 'file', + FORMULA = 'formula', + HOUR = 'hour', + ITEM_ASSIGNEES = 'item_assignees', + ITEM_ID = 'item_id', + LAST_UPDATED = 'last_updated', + LINK = 'link', + LOCATION = 'location', + LONG_TEXT = 'long_text', + MIRROR = 'mirror', + NAME = 'name', + NUMBERS = 'numbers', + PHONE = 'phone', + PEOPLE = 'people', + PROGRESS = 'progress', + RATING = 'rating', + STATUS = 'status', + SUBTASKS = 'subtasks', + TAGS = 'tags', + TEAM = 'team', + TEXT = 'text', + TIMELINE = 'timeline', + TIME_TRACKING = 'time_tracking', + VOTE = 'vote', + WEEK = 'week', + WORLD_CLOCK = 'world_clock', + UNSUPPORTED = 'unsupported', +} + +export const enum MondayWebhookEventType { + CHANGE_COLUMN_VALUE = 'change_column_value', + CHANGE_STATUS_COLUMN_VALUE = 'change_status_column_value', + CHANGE_SUBITEM_COLUMN_VALUE = 'change_subitem_column_value', + CHANGE_SPECIFIC_COLUMN_VALUE = 'change_specific_column_value', + CHANGE_NAME = 'change_name', + CREATE_ITEM = 'create_item', + ITEM_ARCHIVED = 'item_archived', + ITEM_DELETED = 'item_deleted', + ITEM_MOVED_TO_ANY_GROUP = 'item_moved_to_any_group', + ITEM_MOVED_TO_SPECIFIC_GROUP = 'item_moved_to_specific_group', + ITEM_RESTORED = 'item_restored', + CREATE_SUBITEM = 'create_subitem', + CHANGE_SUBITEM_NAME = 'change_subitem_name', + MOVE_SUBITEM = 'move_subitem', + SUBITEM_ARCHIVED = 'subitem_archived', + SUBITEM_DELETED = 'subitem_deleted', + CREATE_COLUMN = 'create_column', + CREATE_UPDATE = 'create_update', + EDIT_UPDATE = 'edit_update', + DELETE_UPDATE = 'delete_update', + CREATE_SUBITEM_UPDATE = 'create_subitem_update', +} +export const enum BoardType { + BOARD = 'board', + SUB_ITEMS_BOARD = 'sub_items_board', +} +export const MondayNotWritableColumnType = [ + MondayColumnType.UNSUPPORTED, + MondayColumnType.AUTO_NUMBER, + MondayColumnType.NAME, + MondayColumnType.COLOR_PICKER, + MondayColumnType.BUTTON, + MondayColumnType.MIRROR, + MondayColumnType.SUBTASKS, + MondayColumnType.ITEM_ID, + MondayColumnType.CREATION_LOG, + MondayColumnType.FILE, + MondayColumnType.FORMULA, + MondayColumnType.DOC, + MondayColumnType.LAST_UPDATED, + MondayColumnType.PROGRESS, + MondayColumnType.TAGS, + MondayColumnType.TIME_TRACKING, + MondayColumnType.VOTE, +]; + +export const COLUMN_TYPE_OPTIONS = [ + { + label: 'Auto Number', + value: MondayColumnType.AUTO_NUMBER, + }, + { + label: 'Color Picker', + value: MondayColumnType.COLOR_PICKER, + }, + { + label: 'Checkbox', + value: MondayColumnType.CHECKBOX, + }, + { + label: 'Country', + value: MondayColumnType.COUNTRY, + }, + { + label: 'Creation Log', + value: MondayColumnType.CREATION_LOG, + }, + { + label: 'Date', + value: MondayColumnType.DATE, + }, + { + label: 'Dropdown', + value: MondayColumnType.DROPDOWN, + }, + { + label: 'Email', + value: MondayColumnType.EMAIL, + }, + { + label: 'Hour', + value: MondayColumnType.HOUR, + }, + { + label: 'Item ID', + value: MondayColumnType.ITEM_ID, + }, + { + label: 'Last Updated', + value: MondayColumnType.LAST_UPDATED, + }, + { + label: 'Link', + value: MondayColumnType.LINK, + }, + { + label: 'Location', + value: MondayColumnType.LOCATION, + }, + { + label: 'Long Text', + value: MondayColumnType.LONG_TEXT, + }, + { + label: 'Numbers', + value: MondayColumnType.NUMBERS, + }, + { + label: 'People', + value: MondayColumnType.PEOPLE, + }, + { + label: 'Phone', + value: MondayColumnType.PHONE, + }, + { + label: 'Progress Tracking', + value: MondayColumnType.PROGRESS, + }, + { + label: 'Rating', + value: MondayColumnType.RATING, + }, + { + label: 'Status', + value: MondayColumnType.STATUS, + }, + { + label: 'Tags', + value: MondayColumnType.TAGS, + }, + { + label: 'Text', + value: MondayColumnType.TEXT, + }, + { + label: 'Timeline', + value: MondayColumnType.TIMELINE, + }, + { + label: 'Time Tracking', + value: MondayColumnType.TIME_TRACKING, + }, + { + label: 'Vote', + value: MondayColumnType.VOTE, + }, + { + label: 'Week', + value: MondayColumnType.WEEK, + }, + { + label: 'World Clock', + value: MondayColumnType.WORLD_CLOCK, + }, +]; diff --git a/packages/pieces/monday/src/lib/common/data.ts b/packages/pieces/monday/src/lib/common/data.ts deleted file mode 100644 index d2f4a6b70..000000000 --- a/packages/pieces/monday/src/lib/common/data.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { HttpMessageBody, HttpMethod, HttpResponse, httpClient } from "@activepieces/pieces-common" -import { Board, BoardResponse, Item, SubItem, Update } from "./types" - -export async function mondayMakeRequest(access_token: string, query: string, method: HttpMethod): Promise> { - return await httpClient.sendRequest({ - url: "https://api.monday.com/v2", - method, - headers: { - 'Authorization': access_token - }, - body: { - query - } - }) -} - -export async function getBoards(access_token: string, workspace_id?: string, limit = 50): Promise { - const query = ` - query { - boards( - ${workspace_id === 'main' ? '' : `workspace_ids: ${workspace_id},`} - limit: ${limit} - ) { id name type } - } - ` - - const response = await mondayMakeRequest( - access_token, - query, - HttpMethod.GET - ) - - return response.body.data.boards -} - -export async function getItems({ - access_token, - board_id, - limit = 10, - newest_first = true -}: { - access_token: string, - board_id?: string, - limit?: number, - since?: number, - newest_first?: boolean -}): Promise { - if (!board_id) return [] - - const query = ` - query { - boards(ids: [${board_id}]) { - id - name - items (limit: ${limit}, newest_first: ${newest_first}) { - id - name - } - } - } - ` - const response = await mondayMakeRequest( - access_token, - query, - HttpMethod.GET - ) - const board = response.body.data.boards?.[0] - - if (board) - return board.items - - return [] -} - -export async function getUpdates({ - access_token, - limit = 10 -}: { - access_token: string, - limit?: number -}): Promise { - const query = ` - query { - updates (limit: ${limit}) { - body - id - created_at - creator { - name - id - } - } - } - ` - const response = await mondayMakeRequest<{ - data: { - updates: Update[] - }, - account_id: number - }>( - access_token, - query, - HttpMethod.GET - ) - - - if (response.status === 200) { - return response.body.data.updates - } - - return [] -} - -export async function getSubitems({ - access_token, - board_id, - item_id, - newest_first = true -}: { - access_token: string, - board_id?: string, - item_id?: string, - limit?: number, - since?: number, - newest_first?: boolean -}): Promise { - if (!board_id) return [] - - const query = ` - query { - boards (ids: [${board_id}]) { - id - name - items (${item_id ? `ids: [${item_id}]` : ``}, newest_first: ${newest_first}) { - id - subitems { - id - name - } - } - } - } - ` - const response = await mondayMakeRequest( - access_token, - query, - HttpMethod.GET - ) - const board = response.body.data.boards?.[0] - - if (board) { - const subitems: SubItem[] = [] - board.items.forEach( - item => { - subitems.concat(item.subitems) - } - ) - return subitems - } - - return [] -} \ No newline at end of file diff --git a/packages/pieces/monday/src/lib/common/helper.ts b/packages/pieces/monday/src/lib/common/helper.ts new file mode 100644 index 000000000..8cfc7520a --- /dev/null +++ b/packages/pieces/monday/src/lib/common/helper.ts @@ -0,0 +1,384 @@ +import { + DynamicPropsValue, + Property, + Validators, +} from '@activepieces/pieces-framework'; +import { isEmpty } from '@activepieces/shared'; +import dayjs from 'dayjs'; +import { MondayColumnType } from './constants'; +import { ColumnValue, MondayColumn } from './models'; + +type ColumnIdTypeMap = { + [key: string]: string; +}; +export function generateColumnIdTypeMap( + columns: MondayColumn[] +): ColumnIdTypeMap { + const result: ColumnIdTypeMap = {}; + for (const column of columns) { + result[column.id] = column.type; + } + return result; +} + +// creates activepiece prop type for monday column +export const convertMondayColumnToActivepiecesProp = (column: MondayColumn) => { + switch (column.type) { + case MondayColumnType.CHECKBOX: + return Property.Checkbox({ + displayName: column.title, + required: false, + }); + case MondayColumnType.BOARD_RELATION: + return Property.ShortText({ + displayName: column.title, + description: + 'A list of item IDs to connect with. The items must be on boards that are connected to the column. Example: [125345, 5846475]', + required: false, + validators: [Validators.pattern(/^\[\d+(,\d+)*]$/)], + }); + case MondayColumnType.COUNTRY: + return Property.ShortText({ + displayName: column.title, + required: false, + description: `The ISO 2-letter code and the country's name are separated by a dash. Example: US-United States`, + }); + case MondayColumnType.DATE: + return Property.DateTime({ + displayName: column.title, + required: false, + description: 'Use YYYY-MM-DD HH:mm:ss format.', + }); + case MondayColumnType.DEPENDENCY: + return Property.ShortText({ + displayName: column.title, + description: + 'A list of item IDs from the same board. Example: [188392, 20339]', + required: false, + validators: [Validators.pattern(/^\[\d+(,\d+)*]$/)], + }); + case MondayColumnType.DROPDOWN: { + const labels: { id: string; name: string }[] = JSON.parse( + column.settings_str + ).labels; + return Property.StaticMultiSelectDropdown({ + displayName: column.title, + required: false, + options: { + disabled: false, + options: + labels.length > 0 + ? labels.map((label) => { + return { + label: label.name, + value: label.name, + }; + }) + : [], + }, + }); + } + case MondayColumnType.EMAIL: + case MondayColumnType.LINK: + case MondayColumnType.TEXT: + return Property.ShortText({ + displayName: column.title, + required: false, + }); + case MondayColumnType.HOUR: + return Property.ShortText({ + displayName: column.title, + required: false, + description: `Represent time in 24-hour format, like '16:30' or '2:00', ensuring removal of leading zeroes from data (e.g., send '9' instead of '09').`, + validators: [Validators.pattern(/^([01]?[0-9]|2[0-3]):([0-5][0-9])$/)], + }); + case MondayColumnType.LOCATION: + return Property.ShortText({ + displayName: column.title, + required: false, + description: `Enter location details in the following format: **latitude|longitude|address(optional)**. For example: "37.7749|-122.4194|San Francisco, CA, USA."`, + }); + case MondayColumnType.LONG_TEXT: + return Property.LongText({ + displayName: column.title, + required: false, + }); + case MondayColumnType.NUMBERS: + return Property.Number({ + displayName: column.title, + required: false, + }); + case MondayColumnType.PHONE: + return Property.ShortText({ + displayName: column.title, + required: false, + description: `Enter your phone number along with the country's ISO 2-letter code, separated by a dash. For ezample, 1234567890-US.`, + }); + case MondayColumnType.RATING: + return Property.Number({ + displayName: column.title, + required: false, + description: `A number between 1 and 5.For example, 3.`, + validators: [Validators.inRange(1, 5)], + }); + case MondayColumnType.STATUS: { + const lables = JSON.parse(column.settings_str).labels; + const options: { label: string; value: string }[] = []; + Object.keys(lables).forEach((key) => { + if (lables[key] !== '') { + options.push({ value: lables[key], label: lables[key] }); + } + }); + return Property.StaticDropdown({ + displayName: column.title, + required: false, + options: { + disabled: false, + options: options, + }, + }); + } + case MondayColumnType.TIMELINE: + return Property.ShortText({ + displayName: column.title, + required: false, + description: `Enter the start and end dates in the YYYY-MM-DD format, separated by a symbol of semicolon(;) symbol. For example: '2022-01-01;2022-12-31`, + validators: [ + Validators.pattern(/^\d{4}-\d{2}-\d{2};\d{4}-\d{2}-\d{2}$/), + ], + }); + case MondayColumnType.WEEK: + return Property.ShortText({ + displayName: column.title, + required: false, + description: `Enter the start and end dates in the YYYY-MM-DD format, separated by a symbol of semicolon(;) symbol. The dates must be 7 days apart (inclusive of the first and last date).\n For example: '2019-06-10;2019-06-16`, + validators: [ + Validators.pattern(/^\d{4}-\d{2}-\d{2};\d{4}-\d{2}-\d{2}$/), + ], + }); + case MondayColumnType.WORLD_CLOCK: + return Property.ShortText({ + displayName: column.title, + required: false, + description: `Enter the timezone in the 'Continent/City' format, for example, Europe/London.`, + }); + default: + return null; + } +}; +export const convertPropValueToMondayColumnValue = ( + columnType: string, + propValue: DynamicPropsValue +) => { + switch (columnType) { + case MondayColumnType.CHECKBOX: + return { + checked: propValue ? 'true' : 'false', + }; + case MondayColumnType.BOARD_RELATION: + case MondayColumnType.DEPENDENCY: + return { + item_ids: JSON.parse(propValue as unknown as string), + }; + case MondayColumnType.COUNTRY: + return { + countryCode: propValue.split('-')[0], + countryName: propValue.split('-')[1], + }; + case MondayColumnType.DATE: { + let datevalue = dayjs(propValue as unknown as string); + if (!datevalue.isValid()) { + datevalue = dayjs(); + } + return { + date: datevalue.format('YYYY-MM-DD'), + time: datevalue.format('HH:mm:ss'), + }; + } + case MondayColumnType.DROPDOWN: + return { + labels: propValue, + }; + case MondayColumnType.EMAIL: + return { + email: propValue, + text: propValue, + }; + case MondayColumnType.HOUR: { + const [hour, minute] = propValue.split(':'); + return { + hour: Number(hour) ?? 0, + minute: Number(minute) ?? 0, + }; + } + case MondayColumnType.LINK: + return { + url: propValue, + text: propValue, + }; + case MondayColumnType.LOCATION: { + const [lat, lng, address] = propValue.split('|'); + return { + lat: lat ?? '', + lng: lng ?? '', + address: address ?? '', + }; + } + case MondayColumnType.LONG_TEXT: + return { + text: propValue, + }; + case MondayColumnType.NUMBERS: + return String(propValue); + case MondayColumnType.PEOPLE: { + const res: { id: string; kind: string }[] = []; + if (Array.isArray(propValue)) { + propValue.forEach((person) => { + res.push({ id: person, kind: 'person' }); + }); + } + return { + personsAndTeams: res, + }; + } + case MondayColumnType.PHONE: { + const [phone, countryCode] = propValue.split('-'); + return { + phone: `+${phone}`, + countryShortName: countryCode, + }; + } + case MondayColumnType.RATING: + return { + rating: Number(propValue), + }; + case MondayColumnType.STATUS: + return { + label: propValue, + }; + case MondayColumnType.TEXT: + return propValue; + case MondayColumnType.TIMELINE: + return { + from: propValue.split(';')[0], + to: propValue.split(';')[1], + }; + case MondayColumnType.WEEK: + return { + startDate: propValue.split(';')[0], + endDate: propValue.split(';')[1], + }; + case MondayColumnType.WORLD_CLOCK: + return { + timezone: propValue, + }; + default: + return null; + } +}; + +export const parseMondayColumnValue = (columnValue: ColumnValue) => { + switch (columnValue.type) { + case MondayColumnType.BUTTON: + return columnValue.label; + case MondayColumnType.CHECKBOX: + return JSON.parse(columnValue.value)?.checked ?? false; + case MondayColumnType.BOARD_RELATION: + return columnValue.linked_item_ids ?? []; + case MondayColumnType.DEPENDENCY: + return JSON.parse(columnValue.linked_item_ids ?? '[]'); + case MondayColumnType.SUBTASKS: { + const res: number[] = []; + if (!isEmpty(JSON.parse(columnValue.value))) { + JSON.parse(columnValue.value).linkedPulseIds.map( + (item: { linkedPulseId: number }) => { + res.push(item.linkedPulseId); + } + ); + } + return res; + } + case MondayColumnType.COLOR_PICKER: + return JSON.parse(columnValue.value)?.color.hex ?? null; + case MondayColumnType.COUNTRY: + return JSON.parse(columnValue.value)?.countryName ?? null; + case MondayColumnType.CREATION_LOG: + return JSON.parse(columnValue.value)?.created_at ?? null; + case MondayColumnType.DATE: { + if (isEmpty(columnValue.value)) { + return null; + } + const dateTime = JSON.parse(columnValue.value); + return `${dateTime.date} ${dateTime.time}`; + } + case MondayColumnType.DOC: + return JSON.parse(columnValue.value)?.files[0].linkToFile ?? null; + case MondayColumnType.DROPDOWN: + return JSON.parse(columnValue.value)?.ids ?? []; + case MondayColumnType.EMAIL: + return JSON.parse(columnValue.value)?.email ?? null; + case MondayColumnType.FILE: + return columnValue.text; + case MondayColumnType.HOUR: { + if (isEmpty(columnValue.value)) { + return null; + } + const hourTime = JSON.parse(columnValue.value); + return `${hourTime.hour}:${hourTime.minute}`; + } + case MondayColumnType.ITEM_ID: + return JSON.parse(columnValue.value)?.item_id ?? null; + case MondayColumnType.LAST_UPDATED: + return JSON.parse(columnValue.value).updated_at; + case MondayColumnType.LINK: + return JSON.parse(columnValue.value)?.url ?? null; + case MondayColumnType.LOCATION: + return JSON.parse(columnValue.value)?.address ?? null; + case MondayColumnType.LONG_TEXT: + return JSON.parse(columnValue.value)?.text ?? null; + case MondayColumnType.MIRROR: + return null; + case MondayColumnType.NUMBERS: + return Number(JSON.parse(columnValue.value)); + case MondayColumnType.PEOPLE: { + const people: number[] = []; + if (!isEmpty(columnValue.value)) { + JSON.parse(columnValue.value).personsAndTeams.map( + (item: { id: number; kind: string }) => { + people.push(item.id); + } + ); + } + return people; + } + case MondayColumnType.PHONE: + return JSON.parse(columnValue.value)?.phone ?? null; + case MondayColumnType.RATING: + return JSON.parse(columnValue.value)?.rating ?? null; + case MondayColumnType.STATUS: + return columnValue.label; + case MondayColumnType.TAGS: + return columnValue.tags.map((item: { name: string }) => item.name); + case MondayColumnType.TEXT: + return JSON.parse(columnValue.value); + case MondayColumnType.TIMELINE: { + if (isEmpty(columnValue.value)) { + return null; + } + const timeline = JSON.parse(columnValue.value); + return { from: timeline.from, to: timeline.to }; + } + case MondayColumnType.TIME_TRACKING: + return JSON.parse(columnValue.value)?.duration ?? null; + case MondayColumnType.VOTE: + return columnValue?.vote_count ?? 0; + case MondayColumnType.WEEK: { + return { + startDate: columnValue.start_date, + endDate: columnValue.end_date, + }; + } + case MondayColumnType.WORLD_CLOCK: + return JSON.parse(columnValue.value)?.timezone ?? null; + } +}; diff --git a/packages/pieces/monday/src/lib/common/index.ts b/packages/pieces/monday/src/lib/common/index.ts new file mode 100644 index 000000000..1849758d5 --- /dev/null +++ b/packages/pieces/monday/src/lib/common/index.ts @@ -0,0 +1,211 @@ +import { DynamicPropsValue, Property } from '@activepieces/pieces-framework'; +import { mondayClient } from './client'; +import { MondayColumnType, MondayNotWritableColumnType } from './constants'; +import { convertMondayColumnToActivepiecesProp } from './helper'; + +export function makeClient(apiKey: string): mondayClient { + return new mondayClient(apiKey); +} + +export const mondayCommon = { + workspace_id: (required = true) => + Property.Dropdown({ + displayName: 'Workspace ID', + required: required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'connect your account first', + options: [], + }; + } + + const client = makeClient(auth as string); + const res = await client.listWorkspcaes(); + return { + disabled: false, + options: res.data.workspaces.map((workspace) => { + return { + label: workspace.name, + value: workspace.id, + }; + }), + }; + }, + }), + board_id: (required = true) => + Property.Dropdown({ + displayName: 'Board ID', + required: required, + refreshers: ['workspace_id'], + options: async ({ auth, workspace_id }) => { + if (!auth || !workspace_id) { + return { + disabled: true, + placeholder: + 'connect your account first and select your workspace.', + options: [], + }; + } + + const client = makeClient(auth as string); + const res = await client.listWorkspaceBoards({ + workspaceId: workspace_id as string, + }); + + return { + disabled: false, + options: res.data.boards + .filter((board) => board.type === 'board') + .map((board) => { + return { + label: board.name, + value: board.id, + }; + }), + }; + }, + }), + group_id: (required = false) => + Property.Dropdown({ + displayName: 'Board Group ID', + required: required, + refreshers: ['board_id'], + options: async ({ auth, board_id }) => { + if (!auth || !board_id) { + return { + disabled: true, + placeholder: + 'connect your account first and select workspace board.', + options: [], + }; + } + const client = makeClient(auth as string); + const res = await client.listBoardGroups({ + boardId: board_id as string, + }); + return { + disabled: false, + options: + res.data.boards.length > 0 + ? res.data.boards[0]?.groups.map((group) => ({ + label: group.title, + value: group.id, + })) + : [], + }; + }, + }), + item_id: (required = true) => + Property.Dropdown({ + displayName: 'Item ID', + required: required, + refreshers: ['board_id'], + options: async ({ auth, board_id }) => { + if (!auth || !board_id) { + return { + disabled: true, + placeholder: + 'connect your account first and select workspace board.', + options: [], + }; + } + const client = makeClient(auth as string); + const res = await client.listBoardItems({ + boardId: board_id as string, + }); + + const items = res.data.boards[0]?.items_page.items; + return { + disabled: false, + options: items.map((item) => { + return { + label: item.name, + value: item.id, + }; + }), + }; + }, + }), + columnIds: (required = true) => + Property.MultiSelectDropdown({ + displayName: 'Column IDs', + description: + 'Limit data output by specifying column IDs; leave empty to display all columns.', + required, + refreshers: ['board_id'], + options: async ({ auth, board_id }) => { + if (!auth || !board_id) { + return { + disabled: true, + placeholder: + 'connect your account first and select workspace board.', + options: [], + }; + } + const client = makeClient(auth as string); + const res = await client.listBoardColumns({ + boardId: board_id as string, + }); + return { + disabled: false, + options: res.data.boards[0].columns.map((column) => { + return { + label: column.title, + value: column.id, + }; + }), + }; + }, + }), + columnValues: Property.DynamicProperties({ + displayName: 'Columns', + required: true, + refreshers: ['board_id'], + props: async ({ auth, board_id }) => { + if (!auth || !board_id) { + return { + disabled: true, + placeholder: 'connect your account first and select workspace board.', + options: [], + }; + } + const fields: DynamicPropsValue = {}; + try { + const client = makeClient(auth as unknown as string); + const res = await client.listBoardColumns({ + boardId: board_id as unknown as string, + }); + const columns = res.data.boards[0]?.columns; + for (const column of columns) { + if (!MondayNotWritableColumnType.includes(column.type)) { + if (column.type === MondayColumnType.PEOPLE) { + const userData = await client.listUsers(); + fields[column.id] = Property.StaticMultiSelectDropdown({ + displayName: column.title, + required: false, + options: { + disabled: false, + options: userData.data.users.map((user) => { + return { + label: `${user.name} (${user.email})`, + value: user.id, + }; + }), + }, + }); + } else { + const prop = convertMondayColumnToActivepiecesProp(column); + if (prop != null) fields[column.id] = prop; + } + } + } + } catch (e) { + console.debug(e); + } + return fields; + }, + }), +}; diff --git a/packages/pieces/monday/src/lib/common/models.ts b/packages/pieces/monday/src/lib/common/models.ts new file mode 100644 index 000000000..6fa5ef9ad --- /dev/null +++ b/packages/pieces/monday/src/lib/common/models.ts @@ -0,0 +1,80 @@ +import { BoardType, MondayColumnType } from './constants'; + +export interface MondayColumn { + id: string; + title: string; + type: MondayColumnType; + description?: string; + settings_str: string; +} +export interface ColumnValue { + id: string; + value: string; + text: string; + type: MondayColumnType; + [key: string]: any; +} + +export type WorkspaceResponse = { + data: { workspaces: Workspace[] }; + account_id: number; +}; + +export interface Workspace { + id: string; + name: string; +} +export interface Group { + id: string; + title: string; +} +export interface SubItem { + id: string; + board: Board; + group: Group; + subscribers: User[]; + name: string; + email: string; + created_at: string; +} +export interface Item { + id: string; + board: Board; + group: Group; + name: string; + email: string; + created_at: string; + column_values: ColumnValue[]; + subitems: SubItem[]; +} +export interface Board { + id: string; + name: string; + groups: Group[]; + type: BoardType; + items_page: { items: Item[] }; +} + +export interface Update { + body: string; + id: string; + created_at: string; + creator: { + name: string; + id: string; + }; +} + +export interface User { + id: string; + name: string; + email: string; + created_at: string; +} + +export type BoardResponse = { data: { boards: Board[] }; account_id: number }; + +export interface WebhookInformation { + id: string; + board_id: string; +} diff --git a/packages/pieces/monday/src/lib/common/mutations.ts b/packages/pieces/monday/src/lib/common/mutations.ts new file mode 100644 index 000000000..0bb859709 --- /dev/null +++ b/packages/pieces/monday/src/lib/common/mutations.ts @@ -0,0 +1,81 @@ +export const mondayGraphQLMutations = { + createItem: ` + mutation createItem( + $itemName: String! + $boardId: ID! + $groupId: String + $columnValues: JSON + $createLabels: Boolean + ) { + create_item( + item_name: $itemName + board_id: $boardId + group_id: $groupId + column_values: $columnValues + create_labels_if_missing: $createLabels + ) { + id + } + }`, + updateItem: ` + mutation updateItem($itemId: ID!, $boardId: ID!, $columnValues: JSON!) { + change_multiple_column_values( + item_id: $itemId + board_id: $boardId + column_values: $columnValues + ) { + id + name + } + }`, + createWebhook: ` + mutation createWebhook( + $boardId: ID! + $url: String! + $event: WebhookEventType! + $config: JSON + ) { + create_webhook( + board_id: $boardId + url: $url + event: $event + config: $config + ) { + id + board_id + } + }`, + deleteWebhook: ` + mutation deleteWebhook($webhookId: ID!) { + delete_webhook(id: $webhookId) { + id + board_id + } + }`, + createColumn: ` + mutation createColumn( + $boardId: ID! + $columnTitle: String! + $columnType: ColumnType! + ) { + create_column( + board_id: $boardId + title: $columnTitle + column_type: $columnType + ) { + id + } + }`, + createGroup: ` + mutation createGroup($boardId: ID!, $groupName: String!) { + create_group(board_id: $boardId, group_name: $groupName) { + id + } + }`, + createUpdate: ` + mutation createUpdate($itemId: ID!, $body: String!) { + create_update(item_id: $itemId, body: $body) { + id + } + }`, +}; diff --git a/packages/pieces/monday/src/lib/common/props.ts b/packages/pieces/monday/src/lib/common/props.ts deleted file mode 100644 index ba56622bf..000000000 --- a/packages/pieces/monday/src/lib/common/props.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { HttpMethod, HttpResponse } from "@activepieces/pieces-common"; -import { OAuth2PropertyValue, Property } from "@activepieces/pieces-framework"; -import { getBoards, getItems, mondayMakeRequest } from "./data"; -import { Board, BoardResponse, BoardType, Item, WorkspaceResponse } from "./types"; - -export const mondayProps = { - workspace_id: (required = false) => Property.Dropdown({ - displayName: "Workspace", - description: "The workspace's unique identifier.", - required: required, - defaultValue: 'main', - refreshers: [], - options: async ({ auth }) => { - if (!auth) return { disabled: true, placeholder: 'connect your account first', options: [] } - - const response: HttpResponse = - await mondayMakeRequest( - (auth as OAuth2PropertyValue).access_token, - `query { workspaces(limit:50) { id name } }`, - HttpMethod.GET - ) - - const options = response.body.data.workspaces - .map((workspace) => ({ label: workspace.name, value: workspace.id })) - - return { - disabled: false, - options: [{ label: 'Main Workspace', value: 'main' }].concat(options) - } - } - }), - board_id: (required = false, refreshers = ['workspace_id']) => Property.Dropdown({ - displayName: "Board", - description: "The board's unique identifier.", - required: required, - refreshers: refreshers, - options: async ({ auth, workspace_id }) => { - if (!auth) return { disabled: true, placeholder: 'connect your account first', options: [] } - - const boards: Board[] = await getBoards( - (auth as OAuth2PropertyValue).access_token, - workspace_id as string - ) - - return { - disabled: false, - options: boards - .filter((value) => value.type === BoardType.BOARD) - .map((board) => ({ label: board['name'] as string, value: board['id'] as string })) - } - } - }), - group_id: (required = false) => Property.Dropdown({ - description: 'Board Group', - displayName: 'Group', - required: required, - refreshers: [ 'board_id'], - options: async ({ auth, board_id }) => { - if (!auth) - return { disabled: true, placeholder: 'connect your account first', options: [] } - if (!board_id) - return { disabled: true, placeholder: 'Select a board first', options: [] } - - const response: HttpResponse = await mondayMakeRequest( - (auth as OAuth2PropertyValue).access_token, - `query { - boards( - ids: ${board_id}, - limit:50 - ) - { groups { id title } } } - `, - HttpMethod.GET - ) - - const boards = response.body?.data.boards - if (!boards) - return { disabled: true, placeholder: 'Error fetching your boards', options: [] } - - return { - disabled: false, - options: ( - boards.length > 0 - ? boards[0].groups.map((group) => ({ label: group.title, value: group.id })) - : [] - ) - } - } - }), - item_id: (required = false) => Property.Dropdown({ - description: 'Board Item', - displayName: 'Item', - required: required, - refreshers: [ 'board_id'], - options: async ({ auth, board_id }) => { - if (!auth) - return { disabled: true, placeholder: 'connect your account first', options: [] } - if (!board_id) - return { disabled: true, placeholder: 'Select a board first', options: [] } - - const items: Item[] = await getItems({ - access_token: (auth as OAuth2PropertyValue).access_token, - board_id: board_id as string - }) - - return { - disabled: false, - options: ( - items.map((item) => ({ label: item.name, value: item.id })) - ) - } - } - }) -} diff --git a/packages/pieces/monday/src/lib/common/queries.ts b/packages/pieces/monday/src/lib/common/queries.ts new file mode 100644 index 000000000..11043ca50 --- /dev/null +++ b/packages/pieces/monday/src/lib/common/queries.ts @@ -0,0 +1,158 @@ +export const mondayGraphQLQueries = { + listWorkspaces: ` + query listWorkspaces($limit: Int) + { + workspaces(limit: $limit) + { + id + name + } + }`, + listWorkspaceBoards: ` + query listWorkspaceBoards($workspaceId: ID) + { + boards(workspace_ids: [$workspaceId], order_by: created_at) + { + id + name + type + } + }`, + listBoardGroups: ` + query listGroups($boardId: ID!) + { + boards(ids: [$boardId]) + { + groups{ + id + title + } + } + }`, + listBoardColumns: ` + query listBoardColumns($boardId: ID!) + { + boards(ids: [$boardId]) + { + columns{ + id + title + type + settings_str + description + } + } + }`, + listBoardItems: ` + query listBoardItems($boardId: ID!) + { + boards(ids: [$boardId]) + { + items_page + { + items{ + id + name + } + } + } + }`, + listUsers: ` + query listUsers + { + users(newest_first: true) + { + id + name + email + } + }`, + getItemColumnValues: ` + query getItemColumnValues($boardId: ID!,$itemId: ID!,$columnIds: [String!]) + { + boards(ids: [$boardId]) + { + items_page(query_params: {ids: [$itemId]}) + { + items{ + id + name + column_values(ids: $columnIds){ + id + type + value + text + ... on ButtonValue{ + label + } + ... on StatusValue{ + label + } + ... on VoteValue{ + vote_count + } + ... on TagsValue{ + tags{ + name + } + } + ... on BoardRelationValue { + linked_item_ids + } + ... on DependencyValue { + linked_item_ids + } + ... on WeekValue { + start_date + end_date + } + } + } + } + } + }`, + getBoardItemValues: ` + query getItemColumnValues($boardId: ID!,$columnIds: [String!]) + { + boards(ids: [$boardId]) + { + items_page(query_params: {order_by: {column_id: "__last_updated__",direction: desc}}) + { + items{ + id + name + column_values(ids: $columnIds){ + id + type + value + text + ... on ButtonValue{ + label + } + ... on StatusValue{ + label + } + ... on VoteValue{ + vote_count + } + ... on TagsValue{ + tags{ + name + } + } + ... on BoardRelationValue { + linked_item_ids + } + ... on DependencyValue { + linked_item_ids + } + ... on WeekValue { + start_date + end_date + } + } + } + } + } + }`, +}; diff --git a/packages/pieces/monday/src/lib/common/types.ts b/packages/pieces/monday/src/lib/common/types.ts deleted file mode 100644 index 3106b3db8..000000000 --- a/packages/pieces/monday/src/lib/common/types.ts +++ /dev/null @@ -1,57 +0,0 @@ -export type BoardResponse = { data: { boards: Board[] }, account_id: number } -export type WorkspaceResponse = { data: { workspaces: Workspace[] }, account_id: number } - -export interface Workspace { - id: string - name: string -} -export interface Board { - id: string - name: string - groups: Group[], - type: BoardType, - items: Item[] -} -export interface Group { - id: string - title: string -} - -export interface Item { - id: string - board: Board - group: Group - name: string - email: string - created_at: string - subitems: SubItem[] -} -export interface Update { - body: string, - id: string, - created_at: string, - creator: { - name: string, - id: string, - } -} -export interface SubItem { - id: string - board: Board - group: Group - subscribers: User[] - name: string - email: string - created_at: string -} -export interface User { - id: string - name: string - email: string - created_at: string -} - -export enum BoardType { - BOARD = 'board', - SUB_ITEMS_BOARD = 'sub_items_board' -} \ No newline at end of file diff --git a/packages/pieces/monday/src/lib/triggers/item-created-trigger.ts b/packages/pieces/monday/src/lib/triggers/item-created-trigger.ts deleted file mode 100644 index b70af3561..000000000 --- a/packages/pieces/monday/src/lib/triggers/item-created-trigger.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { pollingHelper } from "@activepieces/pieces-common" -import { TriggerStrategy, createTrigger } from '@activepieces/pieces-framework' - -import { mondayProps } from '../common/props' -import { itemPolling } from "./polling" -import { mondayAuth } from "../.." - -export const mondayItemCreatedTrigger = createTrigger({ - auth: mondayAuth, - name: `monday_item_created`, - displayName: 'New Item Created', - description: 'Triggered when a new item is created', - props: { - workspace_id: mondayProps.workspace_id(true), - board_id: mondayProps.board_id(true) - }, - sampleData: { - "id": "22222", - "name": "Project" - }, - type: TriggerStrategy.POLLING, - onEnable: async ({ auth, store, propsValue }) => { - await pollingHelper.onEnable(itemPolling, { - auth, store, propsValue - }) - }, - onDisable: async ({ auth, store, propsValue }) => { - await pollingHelper.onDisable(itemPolling, { - auth, store, propsValue - }) - }, - run: async ({ auth, store, propsValue }) => { - return await pollingHelper.poll(itemPolling, { - auth, store, propsValue - }) - }, - test: async ({ auth, store, propsValue }) => { - return await pollingHelper.test(itemPolling, { - auth, store, propsValue - }) - } -}) diff --git a/packages/pieces/monday/src/lib/triggers/new-item-in-board.ts b/packages/pieces/monday/src/lib/triggers/new-item-in-board.ts new file mode 100644 index 000000000..dbfeaa1da --- /dev/null +++ b/packages/pieces/monday/src/lib/triggers/new-item-in-board.ts @@ -0,0 +1,119 @@ +import { + TriggerStrategy, + WebhookHandshakeStrategy, + createTrigger, +} from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; +import { MondayWebhookEventType } from '../common/constants'; +import { parseMondayColumnValue } from '../common/helper'; +import { WebhookInformation } from '../common/models'; + +export const newItemInBoardTrigger = createTrigger({ + auth: mondayAuth, + name: 'monday_new_item_in_board', + displayName: 'New Item in Board', + description: 'Triggers when a new item is created in board.', + props: { + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + }, + type: TriggerStrategy.WEBHOOK, + sampleData: { + event: { + userId: 9603417, + originalTriggerUuid: null, + boardId: 1771812698, + pulseId: 1772099344, + pulseName: 'Create_item webhook', + groupId: 'topics', + groupName: 'Group Title', + groupColor: '#579bfc', + isTopGroup: true, + columnValues: {}, + app: 'monday', + type: 'create_pulse', + triggerTime: '2021-10-11T09:07:28.210Z', + subscriptionId: 73759690, + triggerUuid: 'b5ed2e17c530f43668de130142445cba', + }, + }, + async onEnable(context) { + const { board_id } = context.propsValue; + + const client = makeClient(context.auth as string); + const res = await client.createWebhook({ + boardId: board_id, + url: context.webhookUrl, + event: MondayWebhookEventType.CREATE_ITEM, + }); + await context.store.put( + 'monday_new_item_trigger', + res.data + ); + }, + async onDisable(context) { + const webhook = await context.store.get( + 'monday_new_item_trigger' + ); + if (webhook != null) { + const client = makeClient(context.auth as string); + await client.deleteWebhook({ webhookId: webhook.id }); + } + }, + async run(context) { + const payload = context.payload.body as MondayWebhookPayload; + const transformedValues: Record = {}; + try { + const client = makeClient(context.auth as string); + const res = await client.getItemColumnValues({ + boardId: payload.event.boardId, + itemId: payload.event.pulseId, + }); + const item = res.data.boards[0].items_page.items[0]; + for (const column of item.column_values) { + transformedValues[column.id] = parseMondayColumnValue(column); + } + } catch (e) { + console.error(e); + } + + const enriched = [ + { + ...payload, + columnValues: transformedValues, + }, + ]; + return enriched; + }, + handshakeConfiguration: { + strategy: WebhookHandshakeStrategy.BODY_PARAM_PRESENT, + paramName: 'challenge', + }, + async onHandshake(context) { + return { + status: 200, + body: { challenge: (context.payload.body as any)['challenge'] }, + }; + }, +}); + +interface MondayWebhookPayload { + event: { + userId: number; + originalTriggerUuid: null; + boardId: number; + pulseId: number; + pulseName: string; + groupId: string; + groupName: string; + groupColor: string; + isTopGroup: boolean; + columnValues: Record; + app: string; + type: 'create_pulse'; + triggerTime: string; + subscriptionId: number; + triggerUuid: string; + }; +} diff --git a/packages/pieces/monday/src/lib/triggers/new-update-trigger.ts b/packages/pieces/monday/src/lib/triggers/new-update-trigger.ts deleted file mode 100644 index c4d279e09..000000000 --- a/packages/pieces/monday/src/lib/triggers/new-update-trigger.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { pollingHelper } from "@activepieces/pieces-common" -import { TriggerStrategy, createTrigger } from '@activepieces/pieces-framework' -import { updatesPolling } from "./polling" -import { mondayAuth } from "../.." - -export const mondayNewUpdatesTrigger = createTrigger({ - auth: mondayAuth, - name: `monday_new_updates`, - displayName: 'New Update', - description: 'Triggered when a new update is created', - props: { - }, - sampleData: { - "id": "22222", - "name": "Project" - }, - type: TriggerStrategy.POLLING, - onEnable: async ({ auth, store, propsValue }) => { - await pollingHelper.onEnable(updatesPolling, { - auth, store, propsValue - }) - }, - onDisable: async ({ auth, store, propsValue }) => { - await pollingHelper.onDisable(updatesPolling, { - auth, store, propsValue - }) - }, - run: async ({ auth, store, propsValue }) => { - return await pollingHelper.poll(updatesPolling, { - auth, store, propsValue - }) - }, - test: async ({ auth, store, propsValue }) => { - return await pollingHelper.test(updatesPolling, { - auth, store, propsValue - }) - } -}) diff --git a/packages/pieces/monday/src/lib/triggers/polling.ts b/packages/pieces/monday/src/lib/triggers/polling.ts deleted file mode 100644 index cddbfbab0..000000000 --- a/packages/pieces/monday/src/lib/triggers/polling.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { DedupeStrategy, Polling } from "@activepieces/pieces-common" - -import { getItems, getUpdates } from "../common/data"; -import { OAuth2PropertyValue, PiecePropertyMap, StaticPropsValue } from "@activepieces/pieces-framework"; - -// check for new items in a board -export const itemPolling: Polling = { - strategy: DedupeStrategy.LAST_ITEM, - items: async ({ auth, propsValue }) => { - const items = await getItems({ - access_token: auth.access_token, - board_id: propsValue.board_id - }) - - return items.map((item) => ({ - id: item.id, - data: item, - })) - }, -} - -// check for new updates in items of a board -export const updatesPolling: Polling> = { - strategy: DedupeStrategy.LAST_ITEM, - items: async ({ auth }) => { - const updates = await getUpdates({ - access_token: auth.access_token - }) - return updates.map((item) => ({ - id: item.id, - data: item, - })); - }, -} diff --git a/packages/pieces/monday/src/lib/triggers/specific-column-updated.ts b/packages/pieces/monday/src/lib/triggers/specific-column-updated.ts new file mode 100644 index 000000000..9edce7999 --- /dev/null +++ b/packages/pieces/monday/src/lib/triggers/specific-column-updated.ts @@ -0,0 +1,124 @@ +import { + Property, + TriggerStrategy, + WebhookHandshakeStrategy, + createTrigger, +} from '@activepieces/pieces-framework'; +import { mondayAuth } from '../..'; +import { makeClient, mondayCommon } from '../common'; +import { + MondayNotWritableColumnType, + MondayWebhookEventType, +} from '../common/constants'; +import { WebhookInformation } from '../common/models'; + +export const specificColumnValueUpdatedTrigger = createTrigger({ + auth: mondayAuth, + name: 'monday_specific_column_updated', + displayName: 'Specific Column Value Updated in Board', + description: 'Triggers when a specific column value is updated in board.', + props: { + workspace_id: mondayCommon.workspace_id(true), + board_id: mondayCommon.board_id(true), + column_id: Property.Dropdown({ + displayName: 'Column ID', + required: true, + refreshers: ['board_id'], + options: async ({ auth, board_id }) => { + if (!auth || !board_id) { + return { + disabled: true, + placeholder: + 'connect your account first and select workspace board.', + options: [], + }; + } + const client = makeClient(auth as string); + const res = await client.listBoardColumns({ + boardId: board_id as string, + }); + return { + disabled: false, + options: res.data.boards[0].columns + .filter( + (column) => !MondayNotWritableColumnType.includes(column.type) + ) + .map((column) => { + return { + label: column.title, + value: column.id, + }; + }), + }; + }, + }), + }, + type: TriggerStrategy.WEBHOOK, + sampleData: { + event: { + app: 'monday', + type: 'update_column_value', + triggerTime: '2024-01-08T04:41:55.245Z', + subscriptionId: 3209024, + userId: 53812737, + originalTriggerUuid: null, + boardId: 1835745535, + groupId: 'topics', + pulseId: 1835700420, + pulseName: 'Sample Item', + columnId: 'country', + columnType: 'country', + columnTitle: 'Country', + value: { + countryCode: 'AW', + countryName: 'Aruba', + changed_at: '2024-01-08T04:42:00.109Z', + }, + previousValue: { + changed_at: '2024-01-08T04:41:39.461Z', + countryCode: 'IO', + countryName: 'British Indian Ocean Territory', + }, + changedAt: 1704688953.239433, + isTopGroup: true, + triggerUuid: '72a1ec82ea678e03b55b050711b71e9d', + }, + }, + async onEnable(context) { + const { board_id, column_id } = context.propsValue; + + const client = makeClient(context.auth as string); + const res = await client.createWebhook({ + boardId: board_id, + url: context.webhookUrl, + event: MondayWebhookEventType.CHANGE_SPECIFIC_COLUMN_VALUE, + config: JSON.stringify({ columnId: column_id }), + }); + await context.store.put( + 'monday_specific_column_updated', + res.data + ); + }, + async onDisable(context) { + const webhook = await context.store.get( + 'monday_specific_column_updated' + ); + if (webhook != null) { + const client = makeClient(context.auth as string); + await client.deleteWebhook({ webhookId: webhook.id }); + } + }, + async run(context) { + return [context.payload.body]; + }, + handshakeConfiguration: { + strategy: WebhookHandshakeStrategy.BODY_PARAM_PRESENT, + paramName: 'challenge', + }, + async onHandshake(context) { + return { + status: 200, + body: { challenge: (context.payload.body as any)['challenge'] }, + }; + }, +}); diff --git a/packages/pieces/ntfy/package.json b/packages/pieces/ntfy/package.json index 84e6b6e2e..0c5706450 100644 --- a/packages/pieces/ntfy/package.json +++ b/packages/pieces/ntfy/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-ntfy", - "version": "0.1.2" + "version": "0.1.3" } diff --git a/packages/pieces/ntfy/src/lib/actions/send-notification.ts b/packages/pieces/ntfy/src/lib/actions/send-notification.ts index c97d6d9d5..519bcef5e 100644 --- a/packages/pieces/ntfy/src/lib/actions/send-notification.ts +++ b/packages/pieces/ntfy/src/lib/actions/send-notification.ts @@ -2,6 +2,10 @@ import { createAction, Property } from "@activepieces/pieces-framework"; import { AuthenticationType, httpClient, HttpMethod } from "@activepieces/pieces-common"; import { ntfyAuth } from "../.."; +const encodeToRFC2047 = (text: string) => { + return `=?UTF-8?B?${Buffer.from(text, 'utf-8').toString('base64')}?=`; +} + export const sendNotification = createAction({ auth: ntfyAuth, name: "send_notification", @@ -59,8 +63,10 @@ export const sendNotification = createAction({ const accessToken = auth.access_token; const topic = propsValue.topic; - const title = propsValue.title; - const message = propsValue.message; + let title = propsValue.title; + let message = propsValue.message; + title = encodeToRFC2047(title as string); + message = encodeToRFC2047(message as string); const priority = propsValue.priority; const tags = propsValue.tags; const icon = propsValue.icon; diff --git a/packages/pieces/robolly/.eslintrc.json b/packages/pieces/robolly/.eslintrc.json new file mode 100644 index 000000000..7cd4bf646 --- /dev/null +++ b/packages/pieces/robolly/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts", + "*.tsx", + "*.js", + "*.jsx" + ], + "rules": {} + }, + { + "files": [ + "*.ts", + "*.tsx" + ], + "rules": {} + }, + { + "files": [ + "*.js", + "*.jsx" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/packages/pieces/robolly/README.md b/packages/pieces/robolly/README.md new file mode 100644 index 000000000..54c6e38b1 --- /dev/null +++ b/packages/pieces/robolly/README.md @@ -0,0 +1,7 @@ +# pieces-robolly + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-robolly` to build the library. diff --git a/packages/pieces/robolly/package.json b/packages/pieces/robolly/package.json new file mode 100644 index 000000000..05849d37c --- /dev/null +++ b/packages/pieces/robolly/package.json @@ -0,0 +1,4 @@ +{ + "name": "@activepieces/piece-robolly", + "version": "0.0.1" +} diff --git a/packages/pieces/robolly/project.json b/packages/pieces/robolly/project.json new file mode 100644 index 000000000..e5b5b262b --- /dev/null +++ b/packages/pieces/robolly/project.json @@ -0,0 +1,43 @@ +{ + "name": "pieces-robolly", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/robolly/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/robolly", + "tsConfig": "packages/pieces/robolly/tsconfig.lib.json", + "packageJson": "packages/pieces/robolly/package.json", + "main": "packages/pieces/robolly/src/index.ts", + "assets": [ + "packages/pieces/robolly/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-robolly {args.ver} {args.tag}", + "dependsOn": [ + "build" + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ], + "options": { + "lintFilePatterns": [ + "packages/pieces/robolly/**/*.ts" + ] + } + } + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/pieces/robolly/src/index.ts b/packages/pieces/robolly/src/index.ts new file mode 100644 index 000000000..d5339a124 --- /dev/null +++ b/packages/pieces/robolly/src/index.ts @@ -0,0 +1,31 @@ + +import { createPiece, PieceAuth } from "@activepieces/pieces-framework"; +import { generateImage } from "./lib/actions/generate-image.action"; + +const markdownDescription = ` +Follow these instructions to get your API Key: +1. Visit the following website: https://robolly.com/dashboard/account/ +2. Once on the website, locate and copy your API Key. +Please, take into consideration: We don't test your API Key validity in order to save you some generations, so make sure this is the correct one. +`; + +export const robollyAuth = PieceAuth.SecretText({ + description: markdownDescription, + displayName: 'API Key', + required: true, + validate: async () => { + return { + valid: true, + } + } +}); + +export const robolly = createPiece({ + displayName: "Robolly", + auth: robollyAuth, + minimumSupportedRelease: '0.9.0', + logoUrl: "https://cdn.activepieces.com/pieces/robolly.png", + authors: ["PFernandez98"], + actions: [generateImage], + triggers: [], +}); diff --git a/packages/pieces/robolly/src/lib/actions/generate-image.action.ts b/packages/pieces/robolly/src/lib/actions/generate-image.action.ts new file mode 100644 index 000000000..4be333705 --- /dev/null +++ b/packages/pieces/robolly/src/lib/actions/generate-image.action.ts @@ -0,0 +1,68 @@ +import { HttpMethod, httpClient } from "@activepieces/pieces-common"; +import { Property, createAction } from "@activepieces/pieces-framework"; + +export const generateImage = createAction({ + description: 'Generate an image using Robolly', + displayName: 'Generate Image', + name: 'generate_image', + props: { + template_id: Property.ShortText({ + displayName: 'Template ID', + required: true, + description: 'The ID of the template to use.' + }), + format: Property.StaticDropdown({ + displayName: 'Format', + required: true, + description: 'The format of the image to generate.', + defaultValue: 'jpg', + options: { + "options" : [ + { + "label": "JPG", + "value": 'jpg', + }, + { + "label": "PNG", + "value": 'png', + }, + { + "label": "PDF", + "value": 'pdf', + } + ] + } + }), + modifications: Property.Object({ + displayName: 'Modifications', + description: 'The modifications (fields) to apply to the image.', + required: true, + + }) + }, + async run({ auth, propsValue }){ + + const queryParams: Record = { + }; + + queryParams['json'] = "" + + for (const key in propsValue.modifications) { + const value = propsValue.modifications[key]; + queryParams[key as string] = value as string; + } + + const request = await httpClient.sendRequest({ + method: HttpMethod.GET, + queryParams: queryParams, + url: `https://api.robolly.com/templates/${propsValue.template_id}/render/${propsValue.format}`, + headers: { + 'Authorization': `Bearer ${auth}` + }, + body: propsValue.modifications + }); + + + return request.body; + } +}); diff --git a/packages/pieces/robolly/tsconfig.json b/packages/pieces/robolly/tsconfig.json new file mode 100644 index 000000000..f2400abed --- /dev/null +++ b/packages/pieces/robolly/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/pieces/robolly/tsconfig.lib.json b/packages/pieces/robolly/tsconfig.lib.json new file mode 100644 index 000000000..e583571ea --- /dev/null +++ b/packages/pieces/robolly/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/packages/pieces/schedule/package.json b/packages/pieces/schedule/package.json index 4a8d5a609..0f72dbb93 100644 --- a/packages/pieces/schedule/package.json +++ b/packages/pieces/schedule/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-schedule", - "version": "0.1.2" + "version": "0.1.3" } diff --git a/packages/pieces/schedule/src/lib/triggers/every-x-minutes.trigger.ts b/packages/pieces/schedule/src/lib/triggers/every-x-minutes.trigger.ts index e4b853903..0aedf9f60 100644 --- a/packages/pieces/schedule/src/lib/triggers/every-x-minutes.trigger.ts +++ b/packages/pieces/schedule/src/lib/triggers/every-x-minutes.trigger.ts @@ -1,35 +1,47 @@ -import { Property, TriggerStrategy, Validators, createTrigger } from "@activepieces/pieces-framework"; +import { + Property, + TriggerStrategy, + createTrigger, +} from '@activepieces/pieces-framework'; export const everyXMinutesTrigger = createTrigger({ - name: 'every_x_minutes', - displayName: 'Every X Minutes', - description: 'Triggers the current flow every X minutes', - type: TriggerStrategy.POLLING, - sampleData: {}, - props: { - minutes: Property.Number({ - displayName: "Minutes", - required: true, - defaultValue: 1, - validators: [Validators.minValue(1)] - }) - }, - onEnable: async (ctx) => { - const cronExpression = `*/${ctx.propsValue.minutes} * * * *` - ctx.setSchedule({ - cronExpression: cronExpression, - timezone: 'UTC' - }); - }, - run(ctx) { - const cronExpression = `*/${ctx.propsValue.minutes} * * * *` - return Promise.resolve([{ - cron_expression: cronExpression, - timezone: 'UTC' - }]); - }, - onDisable: async () => { - console.log('onDisable'); - } - + name: 'every_x_minutes', + displayName: 'Every X Minutes', + description: 'Triggers the current flow every X minutes', + type: TriggerStrategy.POLLING, + sampleData: {}, + props: { + minutes: Property.StaticDropdown({ + displayName: 'Minutes', + description: 'Valid value between 1 to 59.', + required: true, + defaultValue: 1, + options: { + disabled: false, + options: Array.from({ length: 59 }, (_, index) => ({ + label: `${index + 1} minute${index !== 0 ? 's' : ''}`, + value: index + 1, + })), + }, + }), + }, + onEnable: async (ctx) => { + const cronExpression = `*/${ctx.propsValue.minutes} * * * *`; + ctx.setSchedule({ + cronExpression: cronExpression, + timezone: 'UTC', + }); + }, + run(ctx) { + const cronExpression = `*/${ctx.propsValue.minutes} * * * *`; + return Promise.resolve([ + { + cron_expression: cronExpression, + timezone: 'UTC', + }, + ]); + }, + onDisable: async () => { + console.log('onDisable'); + }, }); diff --git a/packages/pieces/shopify/package.json b/packages/pieces/shopify/package.json index cc5249015..af4ff3f3f 100644 --- a/packages/pieces/shopify/package.json +++ b/packages/pieces/shopify/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-shopify", - "version": "0.1.3" + "version": "0.1.5" } diff --git a/packages/pieces/shopify/src/index.ts b/packages/pieces/shopify/src/index.ts index b22e301d2..a5946906b 100644 --- a/packages/pieces/shopify/src/index.ts +++ b/packages/pieces/shopify/src/index.ts @@ -1,10 +1,37 @@ - import { PieceAuth, Property, createPiece } from '@activepieces/pieces-framework'; import { newCancelledOrder } from './lib/triggers/new-cancelled-order'; import { newCustomer } from './lib/triggers/new-customer'; import { newOrder } from './lib/triggers/new-order'; import { newPaidOrder } from './lib/triggers/new-paid-order'; -import { HttpMethod, httpClient } from '@activepieces/pieces-common'; +import { HttpMethod } from '@activepieces/pieces-common'; +import { createCustomerAction } from './lib/actions/create-customer'; +import { getCustomerAction } from './lib/actions/get-customer'; +import { updateCustomerAction } from './lib/actions/update-customer'; +import { getCustomerOrdersAction } from './lib/actions/get-customer-orders'; +import { sendShopifyRequest } from './lib/common'; +import { createProductAction } from './lib/actions/create-product'; +import { getProductsAction } from './lib/actions/get-products'; +import { updatedProduct } from './lib/triggers/updated-product'; +import { createDraftOrderAction } from './lib/actions/create-draft-order'; +import { createOrderAction } from './lib/actions/create-order'; +import { createTransactionAction } from './lib/actions/create-transaction'; +import { getTransactionAction } from './lib/actions/get-transaction'; +import { getTransactionsAction } from './lib/actions/get-transactions'; +import { createFulfillmentEventAction } from './lib/actions/create-fulfillment-event'; +import { getFulfillmentsAction } from './lib/actions/get-fulfillments'; +import { closeOrderAction } from './lib/actions/close-order'; +import { cancelOrderAction } from './lib/actions/cancel-order'; +import { getLocationsAction } from './lib/actions/get-locations'; +import { adjustInventoryLevelAction } from './lib/actions/adjust-inventory-level'; +import { getFulfillmentAction } from './lib/actions/get-fulfillment'; +import { createCollectAction } from './lib/actions/create-collect'; +import { updateOrderAction } from './lib/actions/update-order'; +import { updateProductAction } from './lib/actions/update-product'; +import { getAssetAction } from './lib/actions/get-asset'; +import { getProductVariantAction } from './lib/actions/get-product-variant'; +import { getProductAction } from './lib/actions/get-product'; +import { uploadProductImageAction } from './lib/actions/upload-product-image'; +import { newAbandonedCheckout } from './lib/triggers/new-abandoned-checkout'; const markdown = ` To Obtain an Admin Token, follow these steps: @@ -14,7 +41,7 @@ To Obtain an Admin Token, follow these steps: 3. Click on Develop apps 4. Create an App 5. Fill the app name -6. Click on Configure Admin API Scopes (Select the following scopes 'read_orders', 'read_customers') +6. Click on Configure Admin API Scopes (Select the following scopes 'read_orders', 'write_orders', 'write_customers', 'read_customers', 'write_products', 'read_products', 'write_draft_orders', 'read_draft_orders') 7. Click on Install app 8. Copy the Admin Access Token @@ -23,54 +50,79 @@ To Obtain an Admin Token, follow these steps: ` export const shopifyAuth = PieceAuth.CustomAuth({ - description: markdown, - required: true, - props: { - shopName: Property.ShortText({ - displayName: 'Shop Name', - required: true - }), - adminToken: PieceAuth.SecretText({ - displayName: 'Admin Token', - required: true - }) - }, - validate: async ({ auth }) => { - const { shopName, adminToken } = auth; - try { - const response = await httpClient.sendRequest({ - method: HttpMethod.GET, - url: `https://${shopName}.myshopify.com/admin/api/2023-01/shop.json`, - headers: { - "X-Shopify-Access-Token": adminToken + description: markdown, + required: true, + props: { + shopName: Property.ShortText({ + displayName: 'Shop Name', + required: true + }), + adminToken: PieceAuth.SecretText({ + displayName: 'Admin Token', + required: true + }) + }, + validate: async ({ auth }) => { + try { + await sendShopifyRequest({ + auth, + method: HttpMethod.GET, + url: '/shop.json' + }) + return { + valid: true + } + } catch (e) { + return { + valid: false, + error: 'Invalid Shop Name or Admin Token' + } } - }) - return { - valid: true - } - } catch (e) { - return { - valid: false, - error: 'Invalid Shop Name or Admin Token' - } - } - }, + }, }) export const shopify = createPiece({ - displayName: 'Shopify', - logoUrl: 'https://cdn.activepieces.com/pieces/shopify.png', - authors: [ - "abuaboud" - ], - minimumSupportedRelease: '0.5.0', - auth: shopifyAuth, - actions: [ - ], - triggers: [ - newCustomer, - newOrder, - newPaidOrder, - newCancelledOrder - ], + displayName: 'Shopify', + logoUrl: 'https://cdn.activepieces.com/pieces/shopify.png', + authors: [ + "abuaboud", + 'MoShizzle' + ], + minimumSupportedRelease: '0.5.0', + auth: shopifyAuth, + actions: [ + adjustInventoryLevelAction, + cancelOrderAction, + closeOrderAction, + createCollectAction, + createCustomerAction, + createDraftOrderAction, + createFulfillmentEventAction, + createOrderAction, + createProductAction, + createTransactionAction, + getAssetAction, + getCustomerAction, + getCustomerOrdersAction, + getFulfillmentAction, + getFulfillmentsAction, + getLocationsAction, + getProductAction, + getProductVariantAction, + getProductsAction, + getTransactionAction, + getTransactionsAction, + updateCustomerAction, + updateOrderAction, + updateProductAction, + uploadProductImageAction + ], + triggers: [ + newAbandonedCheckout, + newCancelledOrder, + newCustomer, + newOrder, + updatedProduct, + newPaidOrder, + ], }); diff --git a/packages/pieces/shopify/src/lib/actions/adjust-inventory-level.ts b/packages/pieces/shopify/src/lib/actions/adjust-inventory-level.ts new file mode 100644 index 000000000..76c8ec568 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/adjust-inventory-level.ts @@ -0,0 +1,35 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { adjustInventoryLevel } from '../common'; + +export const adjustInventoryLevelAction = createAction({ + auth: shopifyAuth, + name: 'adjust_inventory_level', + displayName: 'Adjust Inventory Level', + description: `Ajust inventory level of an item at a location.`, + props: { + id: Property.Number({ + displayName: 'Inventory Item', + description: 'The ID of the inventory item.', + required: true, + }), + locationId: Property.Number({ + displayName: 'Location', + description: 'The ID of the location.', + required: true, + }), + adjustment: Property.Number({ + displayName: 'Adjustment', + description: 'Positive values increase inventory, negative values decrease it.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { id, locationId, adjustment } = propsValue; + + return await adjustInventoryLevel(id, locationId, adjustment, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/cancel-order.ts b/packages/pieces/shopify/src/lib/actions/cancel-order.ts new file mode 100644 index 000000000..d039e4b32 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/cancel-order.ts @@ -0,0 +1,25 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { cancelOrder } from '../common'; + +export const cancelOrderAction = createAction({ + auth: shopifyAuth, + name: 'cancel_order', + displayName: 'Cancel Order', + description: `Cancel an order.`, + props: { + orderId: Property.Number({ + displayName: 'Order', + description: 'The ID of the order.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { orderId } = propsValue; + + return await cancelOrder(orderId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/close-order.ts b/packages/pieces/shopify/src/lib/actions/close-order.ts new file mode 100644 index 000000000..9549cd7d1 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/close-order.ts @@ -0,0 +1,25 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { closeOrder } from '../common'; + +export const closeOrderAction = createAction({ + auth: shopifyAuth, + name: 'close_order', + displayName: 'Close Order', + description: `Close an order.`, + props: { + orderId: Property.Number({ + displayName: 'Order', + description: 'The ID of the order.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { orderId } = propsValue; + + return await closeOrder(orderId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/create-collect.ts b/packages/pieces/shopify/src/lib/actions/create-collect.ts new file mode 100644 index 000000000..3e9f48ef4 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/create-collect.ts @@ -0,0 +1,33 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { createCollect } from '../common'; + +export const createCollectAction = createAction({ + auth: shopifyAuth, + name: 'create_collect', + displayName: 'Create Collect', + description: `Add a product to a collection.`, + props: { + id: Property.Number({ + displayName: 'Product', + description: 'The ID of the product.', + required: true, + }), + collectionId: Property.Number({ + displayName: 'Collection', + description: 'The ID of the collection.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { id, collectionId } = propsValue; + + return await createCollect({ + product_id: id, + collection_id: collectionId, + }, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/create-customer.ts b/packages/pieces/shopify/src/lib/actions/create-customer.ts new file mode 100644 index 000000000..0e4a237b9 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/create-customer.ts @@ -0,0 +1,66 @@ +import { + Property, + Validators, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { createCustomer } from '../common'; + +export const createCustomerAction = createAction({ + auth: shopifyAuth, + name: 'create_customer', + displayName: 'Create Customer', + description: 'Create a new customer.', + props: { + email: Property.ShortText({ + displayName: 'Email', + required: false, + validators: [Validators.email], + }), + verifiedEmail: Property.Checkbox({ + displayName: 'Verified Email', + description: 'Whether the customer has verified their email.', + required: false, + defaultValue: true, + }), + sendEmailInvite: Property.Checkbox({ + displayName: 'Send Email Invite', + required: false, + defaultValue: false, + }), + firstName: Property.ShortText({ + displayName: 'First Name', + required: false, + }), + lastName: Property.ShortText({ + displayName: 'Last Name', + required: false, + }), + phoneNumber: Property.ShortText({ + displayName: 'Phone Number', + required: false, + validators: [Validators.phoneNumber], + }), + tags: Property.ShortText({ + displayName: 'Tags', + description: 'A string of comma-separated tags for filtering and search', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { email, verifiedEmail, sendEmailInvite, firstName, lastName, phoneNumber, tags } = propsValue; + + return await createCustomer( + { + email, + verified_email: verifiedEmail, + send_email_invite: sendEmailInvite, + first_name: firstName, + last_name: lastName, + phone: phoneNumber, + tags, + }, + auth + ); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/create-draft-order.ts b/packages/pieces/shopify/src/lib/actions/create-draft-order.ts new file mode 100644 index 000000000..80cc0531d --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/create-draft-order.ts @@ -0,0 +1,62 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { createDraftOrder } from '../common'; +import { ShopifyDraftOrder } from '../common/types'; + +export const createDraftOrderAction = createAction({ + auth: shopifyAuth, + name: 'create_draft_order', + displayName: 'Create Draft Order', + description: 'Create a new draft order.', + props: { + productId: Property.Number({ + displayName: 'Product', + description: 'The ID of the product to create the order with.', + required: false, + }), + variantId: Property.Number({ + displayName: 'Product Variant', + description: 'The ID of the variant to create the order with.', + required: false, + }), + title: Property.ShortText({ + displayName: 'Title', + required: false, + }), + quantity: Property.Number({ + displayName: 'Quantity', + required: false, + defaultValue: 1, + }), + price: Property.ShortText({ + displayName: 'Price', + required: false, + }), + customerId: Property.ShortText({ + displayName: 'Customer', + description: 'The ID of the customer to use.', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { productId, variantId, title, quantity, price, customerId } = propsValue; + + const draftOrder: Partial = { + line_items: [ + { + product_id: productId, + variant_id: variantId, + title, + quantity, + price, + } + ] + } + if (customerId) draftOrder.customer = { id: +customerId } + + return await createDraftOrder(draftOrder, auth); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/create-fulfillment-event.ts b/packages/pieces/shopify/src/lib/actions/create-fulfillment-event.ts new file mode 100644 index 000000000..4ff9a425f --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/create-fulfillment-event.ts @@ -0,0 +1,53 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { createFulfillmentEvent } from '../common'; +import { ShopifyFulfillmentEventStatuses } from '../common/types'; + +export const createFulfillmentEventAction = createAction({ + auth: shopifyAuth, + name: 'create_fulfillment_event', + displayName: 'Create Fulfillment Event', + description: 'Create a new fulfillment event.', + props: { + orderId: Property.Number({ + displayName: 'Order', + description: 'The ID of the order.', + required: true, + }), + fulfillmentId: Property.Number({ + displayName: 'Fulfillment', + description: 'The ID of the fulfillment.', + required: true, + }), + status: Property.Dropdown({ + displayName: 'Status', + required: true, + refreshers: [], + options: async () => { + return { + options: Object.values(ShopifyFulfillmentEventStatuses).map((status) => { + return { + label: status.charAt(0).toUpperCase() + status.slice(1).replaceAll('_', ' '), + value: status, + } + }), + } + }, + }), + message: Property.ShortText({ + displayName: 'Message', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { orderId, fulfillmentId, status, message } = propsValue; + + return await createFulfillmentEvent(fulfillmentId, orderId, { + status, + message, + }, auth); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/create-order.ts b/packages/pieces/shopify/src/lib/actions/create-order.ts new file mode 100644 index 000000000..d2900cd6c --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/create-order.ts @@ -0,0 +1,87 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { createOrder } from '../common'; +import { ShopifyOrder } from '../common/types'; + +export const createOrderAction = createAction({ + auth: shopifyAuth, + name: 'create_order', + displayName: 'Create Order', + description: 'Create a new order.', + props: { + productId: Property.Number({ + displayName: 'Product', + description: 'The ID of the product to create the order with.', + required: false, + }), + variantId: Property.Number({ + displayName: 'Product Variant', + description: 'The ID of the variant to create the order with.', + required: false, + }), + title: Property.ShortText({ + displayName: 'Title', + required: false, + }), + quantity: Property.Number({ + displayName: 'Quantity', + required: false, + defaultValue: 1, + }), + price: Property.ShortText({ + displayName: 'Price', + required: false, + }), + customerId: Property.ShortText({ + displayName: 'Customer', + description: 'The ID of the customer to use.', + required: false, + }), + email: Property.ShortText({ + displayName: 'Email', + required: false, + }), + sendReceipt: Property.Checkbox({ + displayName: 'Send Receipt', + required: false, + defaultValue: false, + }), + sendFulfillmentReceipt: Property.Checkbox({ + displayName: 'Send Fulfillment Receipt', + required: false, + defaultValue: false, + }), + tags: Property.ShortText({ + displayName: 'Tags', + description: 'A string of comma-separated tags for filtering and search', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { productId, variantId, title, quantity, price, customerId, email, sendReceipt, sendFulfillmentReceipt, tags } = propsValue; + + const order: Partial = { + line_items: [ + { + product_id: productId, + variant_id: variantId, + title, + quantity, + price, + } + ], + } + if (customerId) order.customer = { id: +customerId } + if (email) { + order.email = email + order.send_receipt = sendReceipt + order.send_fulfillment_receipt = sendFulfillmentReceipt + } + if (tags) order.tags = tags + + return await createOrder(order, auth); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/create-product.ts b/packages/pieces/shopify/src/lib/actions/create-product.ts new file mode 100644 index 000000000..4030f2baa --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/create-product.ts @@ -0,0 +1,87 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { createProduct } from '../common'; +import { ShopifyImage, ShopifyProductStatuses } from '../common/types'; + +export const createProductAction = createAction({ + auth: shopifyAuth, + name: 'create_product', + displayName: 'Create Product', + description: 'Create a new product.', + props: { + title: Property.ShortText({ + displayName: 'Title', + required: true, + }), + bodyHtml: Property.LongText({ + displayName: 'Description', + description: 'Product description (supports HTML)', + required: false, + }), + productType: Property.ShortText({ + displayName: 'Product Type', + description: 'A categorization for the product used for filtering and searching products', + required: false, + }), + productImage: Property.File({ + displayName: 'Product Image', + description: 'The public URL or the base64 image to use', + required: false, + }), + status: Property.StaticDropdown({ + displayName: 'Status', + required: true, + defaultValue: ShopifyProductStatuses.DRAFT, + options: { + options: [ + { + label: 'Active', + value: ShopifyProductStatuses.ACTIVE, + }, + { + label: 'Draft', + value: ShopifyProductStatuses.DRAFT, + }, + { + label: 'Archived', + value: ShopifyProductStatuses.ARCHIVED, + }, + ], + }, + }), + vendor: Property.ShortText({ + displayName: 'Vendor', + required: false, + }), + tags: Property.ShortText({ + displayName: 'Tags', + description: 'A string of comma-separated tags for filtering and search', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { title, bodyHtml, vendor, productType, tags, productImage } = propsValue; + + const images: Partial[] = [] + if (productImage) { + images.push({ + attachment: productImage.base64, + }) + } + + return await createProduct( + { + title, + body_html: bodyHtml, + vendor, + product_type: productType, + tags, + images, + }, + auth, + ); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/create-transaction.ts b/packages/pieces/shopify/src/lib/actions/create-transaction.ts new file mode 100644 index 000000000..f9d927d7e --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/create-transaction.ts @@ -0,0 +1,76 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { createTransaction } from '../common'; +import { ShopifyTransactionKinds } from '../common/types'; + +export const createTransactionAction = createAction({ + auth: shopifyAuth, + name: 'create_transaction', + displayName: 'Create Transaction', + description: 'Create a new transaction.', + props: { + orderId: Property.Number({ + displayName: 'Order', + description: 'The ID of the order to create a transaction for.', + required: true, + }), + kind: Property.Dropdown({ + displayName: 'Type', + required: true, + refreshers: [], + options: async () => { + return { + options: Object.values(ShopifyTransactionKinds).map((kind) => { + return { + label: kind.charAt(0).toUpperCase() + kind.slice(1), + value: kind, + } + }), + } + }, + }), + currency: Property.ShortText({ + displayName: 'Currency', + required: false, + }), + amount: Property.ShortText({ + displayName: 'Amount', + required: false, + }), + authorization: Property.ShortText({ + displayName: 'Authorization Key', + required: false, + }), + parentId: Property.Number({ + displayName: 'Parent ID', + description: 'The ID of an associated transaction.', + required: false, + }), + source: Property.ShortText({ + displayName: 'Source', + description: 'An optional origin of the transaction. Set to external to import a cash transaction for the associated order.', + required: false, + }), + test: Property.Checkbox({ + displayName: 'Test', + description: 'Whether the transaction is a test transaction.', + required: false, + defaultValue: false, + }), + }, + async run({ auth, propsValue }) { + const { orderId, kind, currency, amount, parentId, source, test } = propsValue; + + return await createTransaction(orderId, { + amount, + currency, + kind, + parent_id: parentId, + source, + test, + }, auth); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-asset.ts b/packages/pieces/shopify/src/lib/actions/get-asset.ts new file mode 100644 index 000000000..bdb896299 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-asset.ts @@ -0,0 +1,29 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getAsset } from '../common'; + +export const getAssetAction = createAction({ + auth: shopifyAuth, + name: 'get_asset', + displayName: 'Get Asset', + description: `Get a theme's asset.`, + props: { + key: Property.ShortText({ + displayName: 'Asset Key', + required: true, + }), + themeId: Property.ShortText({ + displayName: 'Theme', + description: 'The ID of the theme.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { key, themeId } = propsValue; + + return await getAsset(key, +themeId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-customer-orders.ts b/packages/pieces/shopify/src/lib/actions/get-customer-orders.ts new file mode 100644 index 000000000..583740bde --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-customer-orders.ts @@ -0,0 +1,24 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getCustomerOrders } from '../common'; + +export const getCustomerOrdersAction = createAction({ + auth: shopifyAuth, + name: 'get_customer_orders', + displayName: 'Get Customer Orders', + description: `Get an existing customer's orders.`, + props: { + customerId: Property.ShortText({ + displayName: 'Customer ID', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { customerId } = propsValue; + + return await getCustomerOrders(customerId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-customer.ts b/packages/pieces/shopify/src/lib/actions/get-customer.ts new file mode 100644 index 000000000..85f58df24 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-customer.ts @@ -0,0 +1,24 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getCustomer } from '../common'; + +export const getCustomerAction = createAction({ + auth: shopifyAuth, + name: 'get_customer', + displayName: 'Get Customer', + description: `Get an existing customer's information.`, + props: { + customerId: Property.ShortText({ + displayName: 'Customer ID', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { customerId } = propsValue; + + return await getCustomer(customerId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-fulfillment.ts b/packages/pieces/shopify/src/lib/actions/get-fulfillment.ts new file mode 100644 index 000000000..2a67749f9 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-fulfillment.ts @@ -0,0 +1,30 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getFulfillment } from '../common'; + +export const getFulfillmentAction = createAction({ + auth: shopifyAuth, + name: 'get_fulfillment', + displayName: 'Get Fulfillment', + description: `Get a fulfillment.`, + props: { + orderId: Property.Number({ + displayName: 'Order', + description: 'The ID of the order.', + required: true, + }), + fulfillmentId: Property.Number({ + displayName: 'Fulfillment', + description: 'The ID of the fulfillment.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { orderId, fulfillmentId } = propsValue + + return await getFulfillment(fulfillmentId, orderId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-fulfillments.ts b/packages/pieces/shopify/src/lib/actions/get-fulfillments.ts new file mode 100644 index 000000000..19e52bcab --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-fulfillments.ts @@ -0,0 +1,25 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getFulfillments } from '../common'; + +export const getFulfillmentsAction = createAction({ + auth: shopifyAuth, + name: 'get_fulfillments', + displayName: 'Get Fulfillments', + description: `Get an order's fulfillments.`, + props: { + orderId: Property.Number({ + displayName: 'Order', + description: 'The ID of the order.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { orderId } = propsValue; + + return await getFulfillments(orderId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-locations.ts b/packages/pieces/shopify/src/lib/actions/get-locations.ts new file mode 100644 index 000000000..2bf81cb1a --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-locations.ts @@ -0,0 +1,14 @@ +import { createAction } from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getLocations } from '../common'; + +export const getLocationsAction = createAction({ + auth: shopifyAuth, + name: 'get_locations', + displayName: 'Get Locations', + description: `Get locations.`, + props: {}, + async run({ auth }) { + return await getLocations(auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-product-variant.ts b/packages/pieces/shopify/src/lib/actions/get-product-variant.ts new file mode 100644 index 000000000..efd8db133 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-product-variant.ts @@ -0,0 +1,25 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getProductVariant } from '../common'; + +export const getProductVariantAction = createAction({ + auth: shopifyAuth, + name: 'get_product_variant', + displayName: 'Get Product Variant', + description: `Get a product variant.`, + props: { + id: Property.ShortText({ + displayName: 'Product Variant', + description: 'The ID of the product variant.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { id } = propsValue; + + return await getProductVariant(+id, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-product.ts b/packages/pieces/shopify/src/lib/actions/get-product.ts new file mode 100644 index 000000000..238d86e0d --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-product.ts @@ -0,0 +1,25 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getProduct } from '../common'; + +export const getProductAction = createAction({ + auth: shopifyAuth, + name: 'get_product', + displayName: 'Get Product', + description: `Get existing product.`, + props: { + id: Property.ShortText({ + displayName: 'Product', + description: 'The ID of the product.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { id } = propsValue; + + return await getProduct(+id, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-products.ts b/packages/pieces/shopify/src/lib/actions/get-products.ts new file mode 100644 index 000000000..c003691e1 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-products.ts @@ -0,0 +1,26 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getProducts } from '../common'; + +export const getProductsAction = createAction({ + auth: shopifyAuth, + name: 'get_products', + displayName: 'Get Products', + description: `Get existing products by title.`, + props: { + title: Property.ShortText({ + displayName: 'Title', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { title } = propsValue; + + return await getProducts(auth, { + title + }) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-transaction.ts b/packages/pieces/shopify/src/lib/actions/get-transaction.ts new file mode 100644 index 000000000..5473e1ee3 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-transaction.ts @@ -0,0 +1,30 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getTransaction } from '../common'; + +export const getTransactionAction = createAction({ + auth: shopifyAuth, + name: 'get_transaction', + displayName: 'Get Transaction', + description: `Get an existing transaction's information.`, + props: { + orderId: Property.Number({ + displayName: 'Order', + description: 'The ID of the order.', + required: true, + }), + transactionId: Property.Number({ + displayName: 'Transaction', + description: 'The ID of the transaction', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { orderId, transactionId } = propsValue; + + return await getTransaction(transactionId, orderId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/get-transactions.ts b/packages/pieces/shopify/src/lib/actions/get-transactions.ts new file mode 100644 index 000000000..ba5ef7cd4 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/get-transactions.ts @@ -0,0 +1,25 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { getTransactions } from '../common'; + +export const getTransactionsAction = createAction({ + auth: shopifyAuth, + name: 'get_transactions', + displayName: 'Get Order Transactions', + description: `Get an order's transactions.`, + props: { + orderId: Property.Number({ + displayName: 'Order', + description: 'The ID of the order.', + required: true, + }), + }, + async run({ auth, propsValue }) { + const { orderId } = propsValue; + + return await getTransactions(orderId, auth) + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/update-customer.ts b/packages/pieces/shopify/src/lib/actions/update-customer.ts new file mode 100644 index 000000000..b5be64e63 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/update-customer.ts @@ -0,0 +1,71 @@ +import { + Property, + Validators, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { updateCustomer } from '../common'; + +export const updateCustomerAction = createAction({ + auth: shopifyAuth, + name: 'update_customer', + displayName: 'Update Customer', + description: 'Update an existing customer.', + props: { + customerId: Property.ShortText({ + displayName: 'Customer ID', + required: true, + }), + email: Property.ShortText({ + displayName: 'Email', + required: false, + validators: [Validators.email], + }), + verifiedEmail: Property.Checkbox({ + displayName: 'Verified Email', + description: 'Whether the customer has verified their email.', + required: false, + defaultValue: true, + }), + sendEmailInvite: Property.Checkbox({ + displayName: 'Send Email Invite', + required: false, + defaultValue: false, + }), + firstName: Property.ShortText({ + displayName: 'First Name', + required: false, + }), + lastName: Property.ShortText({ + displayName: 'Last Name', + required: false, + }), + phoneNumber: Property.ShortText({ + displayName: 'Phone Number', + required: false, + validators: [Validators.phoneNumber], + }), + tags: Property.ShortText({ + displayName: 'Tags', + description: 'A string of comma-separated tags for filtering and search', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { customerId, email, verifiedEmail, sendEmailInvite, firstName, lastName, phoneNumber, tags } = propsValue; + + return await updateCustomer( + customerId, + { + email, + verified_email: verifiedEmail, + send_email_invite: sendEmailInvite, + first_name: firstName, + last_name: lastName, + phone: phoneNumber, + tags, + }, + auth + ); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/update-order.ts b/packages/pieces/shopify/src/lib/actions/update-order.ts new file mode 100644 index 000000000..c1927c640 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/update-order.ts @@ -0,0 +1,54 @@ +import { + Property, + Validators, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { updateOrder } from '../common'; + +export const updateOrderAction = createAction({ + auth: shopifyAuth, + name: 'update_order', + displayName: 'Update Order', + description: 'Update an existing order.', + props: { + id: Property.ShortText({ + displayName: 'Order', + description: 'The ID of the order.', + required: true, + }), + email: Property.ShortText({ + displayName: 'Email', + required: false, + validators: [Validators.email], + }), + phoneNumber: Property.ShortText({ + displayName: 'Phone Number', + required: false, + validators: [Validators.phoneNumber], + }), + tags: Property.ShortText({ + displayName: 'Tags', + description: 'A string of comma-separated tags for filtering and search', + required: false, + }), + note: Property.ShortText({ + displayName: 'Note', + required: false, + }) + }, + async run({ auth, propsValue }) { + const { id, email, phoneNumber, tags, note } = propsValue; + + return await updateOrder( + +id, + { + email, + phone: phoneNumber, + tags, + note, + }, + auth + ); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/update-product.ts b/packages/pieces/shopify/src/lib/actions/update-product.ts new file mode 100644 index 000000000..69ad029cc --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/update-product.ts @@ -0,0 +1,93 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { updateProduct } from '../common'; +import { ShopifyImage, ShopifyProductStatuses } from '../common/types'; + +export const updateProductAction = createAction({ + auth: shopifyAuth, + name: 'update_product', + displayName: 'Update Product', + description: 'Update an existing product.', + props: { + id: Property.ShortText({ + displayName: 'Product', + description: 'The ID of the product.', + required: true, + }), + title: Property.ShortText({ + displayName: 'Title', + required: false, + }), + bodyHtml: Property.LongText({ + displayName: 'Description', + description: 'Product description (supports HTML)', + required: false, + }), + productType: Property.ShortText({ + displayName: 'Product Type', + description: 'A categorization for the product used for filtering and searching products', + required: false, + }), + productImage: Property.File({ + displayName: 'Product Image', + description: 'The public URL or the base64 image to use', + required: false, + }), + status: Property.StaticDropdown({ + displayName: 'Status', + required: true, + defaultValue: ShopifyProductStatuses.DRAFT, + options: { + options: [ + { + label: 'Active', + value: ShopifyProductStatuses.ACTIVE, + }, + { + label: 'Draft', + value: ShopifyProductStatuses.DRAFT, + }, + { + label: 'Archived', + value: ShopifyProductStatuses.ARCHIVED, + }, + ], + }, + }), + vendor: Property.ShortText({ + displayName: 'Vendor', + required: false, + }), + tags: Property.ShortText({ + displayName: 'Tags', + description: 'A string of comma-separated tags for filtering and search', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { id, title, bodyHtml, vendor, productType, tags, productImage } = propsValue; + + const images: Partial[] = [] + if (productImage) { + images.push({ + attachment: productImage.base64, + }) + } + + return await updateProduct( + +id, + { + title, + body_html: bodyHtml, + vendor, + product_type: productType, + tags, + images, + }, + auth, + ); + }, +}); diff --git a/packages/pieces/shopify/src/lib/actions/upload-product-image.ts b/packages/pieces/shopify/src/lib/actions/upload-product-image.ts new file mode 100644 index 000000000..4718b7e16 --- /dev/null +++ b/packages/pieces/shopify/src/lib/actions/upload-product-image.ts @@ -0,0 +1,35 @@ +import { + Property, + createAction, +} from '@activepieces/pieces-framework'; +import { shopifyAuth } from '../..'; +import { createProductImage } from '../common'; + +export const uploadProductImageAction = createAction({ + auth: shopifyAuth, + name: 'upload_product_image', + displayName: 'Upload Product Image', + description: 'Upload a new product image.', + props: { + id: Property.ShortText({ + displayName: 'Product', + description: 'The ID of the product.', + required: true, + }), + image: Property.File({ + displayName: 'Image', + description: 'The public URL or the base64 image to use', + required: true, + }), + position: Property.Number({ + displayName: 'Position', + description: '1 makes it the main image.', + required: false, + }), + }, + async run({ auth, propsValue }) { + const { id, image, position } = propsValue; + + return await createProductImage(+id, { attachment: image.base64, position }, auth); + }, +}); diff --git a/packages/pieces/shopify/src/lib/common/index.ts b/packages/pieces/shopify/src/lib/common/index.ts new file mode 100644 index 000000000..25f695d1e --- /dev/null +++ b/packages/pieces/shopify/src/lib/common/index.ts @@ -0,0 +1,353 @@ +import { HttpMessageBody, HttpMethod, HttpResponse, QueryParams, httpClient } from "@activepieces/pieces-common" +import { ShopifyAuth, ShopifyCheckout, ShopifyCollect, ShopifyCustomer, ShopifyDraftOrder, ShopifyFulfillment, ShopifyFulfillmentEvent, ShopifyImage, ShopifyOrder, ShopifyProduct, ShopifyProductVariant, ShopifyTransaction } from "./types" + +export function getBaseUrl(shopName: string) { + return `https://${shopName}.myshopify.com/admin/api/2023-10` +} + +export function sendShopifyRequest(data: { + url: string, + method: HttpMethod, + body?: HttpMessageBody, + queryParams?: QueryParams, + auth: ShopifyAuth +}): Promise> { + return httpClient.sendRequest({ + url: `${getBaseUrl(data.auth.shopName)}${data.url}`, + method: data.method, + body: data.body, + queryParams: data.queryParams, + headers: { + "X-Shopify-Access-Token": data.auth.adminToken + }, + }) +} + +export async function createCustomer(customer: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: '/customers.json', + method: HttpMethod.POST, + body: { + customer, + }, + }) + + return (response.body as { customer: ShopifyCustomer }).customer +} + +export async function getCustomer(id: string, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/customers/${id}.json`, + method: HttpMethod.GET, + }) + + return (response.body as { customer: ShopifyCustomer }).customer +} + +export async function updateCustomer(id: string, customer: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/customers/${id}.json`, + method: HttpMethod.PUT, + body: { + customer, + }, + }) + + return (response.body as { customer: ShopifyCustomer }).customer +} + +export async function getCustomerOrders(id: string, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/customers/${id}/orders.json`, + method: HttpMethod.GET, + }) + + return (response.body as { orders: ShopifyOrder[] }).orders +} + +export async function getProducts(auth: ShopifyAuth, search: { + title?: string, + createdAtMin?: string, + updatedAtMin?: string, +}): Promise { + const queryParams: QueryParams = {} + const { title, createdAtMin, updatedAtMin } = search + if (title) { + queryParams.title = title + } + if (createdAtMin) { + queryParams.created_at_min = createdAtMin + } + if (updatedAtMin) { + queryParams.updated_at_min = updatedAtMin + } + + const response = await sendShopifyRequest({ + auth: auth, + url: `/products.json`, + method: HttpMethod.GET, + queryParams, + }) + + return (response.body as { products: ShopifyProduct[] }).products +} + +export async function createProduct(product: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/products.json`, + method: HttpMethod.POST, + body: { + product + } + }) + + return (response.body as { product: ShopifyProduct }).product +} + +export async function updateProduct(id: number, product: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/products/${id}.json`, + method: HttpMethod.PUT, + body: { + product + } + }) + + return (response.body as { product: ShopifyProduct }).product +} + +export async function createDraftOrder(draftOrder: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/draft_orders.json`, + method: HttpMethod.POST, + body: { + draft_order: draftOrder, + } + }) + + return (response.body as { draft_order: ShopifyDraftOrder }).draft_order +} + +export async function createOrder(order: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders.json`, + method: HttpMethod.POST, + body: { + order, + } + }) + + return (response.body as { order: ShopifyOrder }).order +} + +export async function updateOrder(id: number, order: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${id}.json`, + method: HttpMethod.PUT, + body: { + order, + }, + }) + + return (response.body as { order: ShopifyOrder }).order +} + +export async function createTransaction(orderId: number, transaction: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${orderId}/transactions.json`, + method: HttpMethod.POST, + body: { + transaction, + } + }) + + return (response.body as { transaction: ShopifyTransaction }).transaction +} + +export async function getTransaction(id: number, orderId: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${orderId}/transactions/${id}.json`, + method: HttpMethod.GET, + }) + + return (response.body as { transaction: ShopifyTransaction }).transaction +} + +export async function getTransactions(orderId: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${orderId}/transactions.json`, + method: HttpMethod.GET, + }) + + return (response.body as { transactions: ShopifyTransaction[] }).transactions +} + +export async function getFulfillments(orderId: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${orderId}/fulfillments.json`, + method: HttpMethod.GET, + }) + + return (response.body as { fulfillments: ShopifyFulfillment[] }).fulfillments +} + +export async function getFulfillment(id: number, orderId: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${orderId}/fulfillments/${id}.json`, + method: HttpMethod.GET, + }) + + return (response.body as { fulfillment: ShopifyFulfillment }).fulfillment +} + +export async function getLocations(auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/locations.json`, + method: HttpMethod.GET, + }) + + return (response.body as { locations: unknown[] }).locations +} + +export async function createFulfillmentEvent(id: number, orderId: number, event: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${orderId}/fulfillments/${id}/events.json`, + method: HttpMethod.POST, + body: { + event, + } + }) + + return (response.body as { fulfillment_event: ShopifyFulfillmentEvent }).fulfillment_event +} + +export async function closeOrder(id: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${id}/close.json`, + method: HttpMethod.POST, + body: {} + }) + + return (response.body as { order: ShopifyOrder }).order +} + +export async function cancelOrder(id: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/orders/${id}/cancel.json`, + method: HttpMethod.POST, + body: {} + }) + + return (response.body as { order: ShopifyOrder }).order +} + +export async function adjustInventoryLevel(id: number, locationId: number, adjustment: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/inventory_levels/adjust.json`, + method: HttpMethod.POST, + body: { + inventory_item_id: id, + location_id: locationId, + available_adjustment: adjustment, + } + }) + + return (response.body as { order: unknown }).order +} + +export async function createCollect(collect: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/collects.json`, + method: HttpMethod.POST, + body: { + collect, + } + }) + + return (response.body as { collect: ShopifyCollect }).collect +} + +export async function getAsset(key: string, themeId: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/themes/${themeId}/assets.json`, + queryParams: { + key, + }, + method: HttpMethod.GET, + }) + + return (response.body as { asset: unknown }).asset +} + +export async function getProductVariant(id: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/variants/${id}.json`, + method: HttpMethod.GET, + }) + + return (response.body as { variant: ShopifyProductVariant }).variant +} + +export async function getProduct(id: number, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/products/${id}.json`, + method: HttpMethod.GET, + }) + + return (response.body as { product: ShopifyProduct }).product +} + +export async function createProductImage(id: number, image: Partial, auth: ShopifyAuth): Promise { + const response = await sendShopifyRequest({ + auth: auth, + url: `/products/${id}/images.json`, + method: HttpMethod.POST, + body: { + image, + } + }) + + return (response.body as { image: ShopifyImage }).image +} + +export async function getAbandonedCheckouts(auth: ShopifyAuth, search: { + sinceId: string, +}): Promise { + const queryParams: QueryParams = {} + const { sinceId } = search + if (sinceId) { + queryParams.since_id = sinceId + } + + const response = await sendShopifyRequest({ + auth: auth, + url: `/checkouts.json`, + method: HttpMethod.GET, + queryParams, + }) + + return (response.body as { checkouts: ShopifyCheckout[] }).checkouts +} \ No newline at end of file diff --git a/packages/pieces/shopify/src/lib/common/register-webhook.ts b/packages/pieces/shopify/src/lib/common/register-webhook.ts index cd93b21ba..a038971d3 100644 --- a/packages/pieces/shopify/src/lib/common/register-webhook.ts +++ b/packages/pieces/shopify/src/lib/common/register-webhook.ts @@ -17,55 +17,55 @@ export const createShopifyWebhookTrigger = ({ }): Trigger => createTrigger({ auth: shopifyAuth, - name, - description, - displayName, - props: { - }, - sampleData: sampleData, - type: TriggerStrategy.WEBHOOK, - async onEnable(context) { - const shopName = context.auth.shopName; - const response = await httpClient.sendRequest<{ + name, + description, + displayName, + props: { + }, + sampleData: sampleData, + type: TriggerStrategy.WEBHOOK, + async onEnable(context) { + const shopName = context.auth.shopName; + const response = await httpClient.sendRequest<{ + webhook: { + id: string + } + }>({ + method: HttpMethod.POST, + url: `https://${shopName}.myshopify.com/admin/api/2023-01/webhooks.json`, + headers: { + "X-Shopify-Access-Token": context.auth.adminToken + }, + body: { webhook: { - id: string + topic: topic, + address: context.webhookUrl, + format: "json" } - }>({ - method: HttpMethod.POST, - url: `https://${shopName}.myshopify.com/admin/api/2023-01/webhooks.json`, - headers: { - "X-Shopify-Access-Token": context.auth.adminToken - }, - body: { - webhook: { - topic: topic, - address: context.webhookUrl, - format: "json" - } - } - }) - await context.store?.put(`shopify_webhook_id`, response.body.webhook.id) - console.log("webhook created", response.body.webhook.id); - }, - async onDisable(context) { - const webhookId = await context.store.get(`shopify_webhook_id`); - const shopName = context.auth.shopName; - await httpClient.sendRequest<{ - webhook: { - id: string - } - }>({ - method: HttpMethod.DELETE, - url: `https://${shopName}.myshopify.com/admin/api/2023-01/webhooks/${webhookId}.json`, - headers: { - "X-Shopify-Access-Token": context.auth.adminToken - } - }) - await context.store?.put(`shopify_webhook_id`, null) - }, - async run(context) { - console.debug("trigger running", context) - return [context.payload.body] - } + } + }) + await context.store?.put(`shopify_webhook_id`, response.body.webhook.id) + console.log("webhook created", response.body.webhook.id); + }, + async onDisable(context) { + const webhookId = await context.store.get(`shopify_webhook_id`); + const shopName = context.auth.shopName; + await httpClient.sendRequest<{ + webhook: { + id: string + } + }>({ + method: HttpMethod.DELETE, + url: `https://${shopName}.myshopify.com/admin/api/2023-01/webhooks/${webhookId}.json`, + headers: { + "X-Shopify-Access-Token": context.auth.adminToken + } + }) + await context.store?.put(`shopify_webhook_id`, null) + }, + async run(context) { + console.debug("trigger running", context) + return [context.payload.body] + } }) diff --git a/packages/pieces/shopify/src/lib/common/types.ts b/packages/pieces/shopify/src/lib/common/types.ts new file mode 100644 index 000000000..a53aea501 --- /dev/null +++ b/packages/pieces/shopify/src/lib/common/types.ts @@ -0,0 +1,185 @@ +export type ShopifyAuth = { + shopName: string + adminToken: string +} + +export type ShopifyCustomer = { + id: number + email: string + accepts_marketing: boolean + created_at: string + updated_at: string + first_name: string + last_name: string + orders_count: number + state: string + total_spent: number + last_order_id: number + note: unknown + verified_email: boolean + tax_exempt: boolean + tags: string + last_order_name: unknown + currency: string + phone: string + addresses: unknown[] + accepts_marketing_updated_at: string + marketing_opt_in_level: unknown + tax_exemptions: unknown[] + email_marketing_consent: unknown + sms_marketing_consent: unknown + send_email_invite: boolean +} + +export type ShopifyOrder = { + line_items: Partial[] + customer: Partial + financial_status: ShopifyOrderFinancialStatuses + email: string + send_receipt: boolean + send_fulfillment_receipt: boolean + fulfillment_status: string + tags: string + phone: string + note: string +} + +export type ShopifyAddress = { + first_name: string + address1: string + phone: string + city: string + zip: string + province: string + country: string + last_name: string + address2: string + company: unknown + latitude: number + longitude: number + name: string + country_code: string + province_code: string +} + +export type ShopifyProduct = { + id: number + title: string + body_html: string + vendor: string + product_type: string + status: ShopifyProductStatuses + tags: string + images: Partial[] + created_at: string + updated_at: string +} + +export type ShopifyImage = { + src: string + attachment: string + position: number +} + +export type ShopifyProductVariant = { + id: number + product_id: number + title: string + price: string + sku: string + inventory_item_id: number + inventory_quantity: number + created_at: string + updated_at: string + [key: string]: string | number | unknown +} + +export type ShopifyLineItem = { + variant_id: number + product_id: number + quantity: number + price: string + title: string +} + +export type ShopifyDraftOrder = ShopifyOrder + +export type ShopifyTransaction = { + order_id: number + currency: string + amount: string + source: string + kind: ShopifyTransactionKinds + parent_id: number + test: boolean +} + +export type ShopifyFulfillment = { + order_id: number + status: ShopifyFulfillmentStatuses + line_items: Partial[] +} + +export type ShopifyFulfillmentEvent = { + id: number + order_id: number + status: ShopifyFulfillmentEventStatuses + message: string +} + +export type ShopifyCollect = { + product_id: number + collection_id: number +} + +export type ShopifyCheckout = { + id: number + abandoned_checkout_url: string + completed_at: string + created_at: string + currency: string + customer: Partial +} + +export enum ShopifyProductStatuses { + ACTIVE = 'active', + ARCHIVED = 'archived', + DRAFT = 'draft', +} + +export enum ShopifyOrderFinancialStatuses { + PENDING = 'pending', + PARTIALLY_PAID = 'partially_pad', +} + +export enum ShopifyTransactionKinds { + AUTHORIZATION = 'authorization', + SALE = 'sale', + CAPTURE = 'capture', + VOID = 'void', + REFUND = 'refund', +} + +export enum ShopifyFulfillmentStatuses { + PENDING = 'pending', + OPEN = 'open', + SUCCESS = 'success', + CANCELLED = 'cancelled', + ERROR = 'error', + FAILURE = 'failure', +} + +export enum ShopifyFulfillmentEventStatuses { + ATTEMPTED_DELIVERY = 'attempted_delivery', + CARRIER_PICKED_UP = 'carrier_picked_up', + CONFIRMED = 'confirmed', + DELAYED = 'delayed', + DELIVERED = 'delivered', + FAILURE = 'failure', + IN_TRANSIT = 'in_transit', + LABEL_PRINTED = 'label_printed', + LABEL_PURCHASED = 'label_purchased', + OUT_FOR_DELIVERY = 'out_for_delivery', + PICKED_UP = 'picked_up', + READY_FOR_PICKUP = 'ready_for_pickup', +} \ No newline at end of file diff --git a/packages/pieces/shopify/src/lib/triggers/new-abandoned-checkout.ts b/packages/pieces/shopify/src/lib/triggers/new-abandoned-checkout.ts new file mode 100644 index 000000000..46ef3dab0 --- /dev/null +++ b/packages/pieces/shopify/src/lib/triggers/new-abandoned-checkout.ts @@ -0,0 +1,40 @@ +import { Polling, DedupeStrategy, pollingHelper } from "@activepieces/pieces-common"; +import { TriggerStrategy, createTrigger } from "@activepieces/pieces-framework"; +import { getAbandonedCheckouts } from "../common"; +import { shopifyAuth } from "../.."; +import { ShopifyAuth } from "../common/types"; + +export const newAbandonedCheckout = createTrigger({ + name: 'new_abandoned_checkout', + auth: shopifyAuth, + displayName: 'New Abandoned Checkout', + description: 'Triggers when a checkout is abandoned.', + props: {}, + sampleData: {}, + type: TriggerStrategy.POLLING, + async onEnable({ auth, propsValue, store }) { + await pollingHelper.onEnable(polling, { auth, propsValue, store }) + }, + async onDisable({ auth, propsValue, store }) { + await pollingHelper.onEnable(polling, { auth, propsValue, store }) + }, + async run({ auth, propsValue, store }) { + return await pollingHelper.poll(polling, { auth, propsValue, store }) + }, + async test({ auth, propsValue, store }) { + return await pollingHelper.test(polling, { auth, propsValue, store }) + } +}) + +const polling: Polling = { + strategy: DedupeStrategy.LAST_ITEM, + items: async ({ auth, lastItemId }) => { + const checkouts = await getAbandonedCheckouts(auth, { + sinceId: lastItemId as string, + }); + return checkouts.map((checkout) => ({ + id: checkout.id, + data: checkout, + })); + } +} \ No newline at end of file diff --git a/packages/pieces/shopify/src/lib/triggers/updated-product.ts b/packages/pieces/shopify/src/lib/triggers/updated-product.ts new file mode 100644 index 000000000..7b725d033 --- /dev/null +++ b/packages/pieces/shopify/src/lib/triggers/updated-product.ts @@ -0,0 +1,89 @@ +import { createShopifyWebhookTrigger } from "../common/register-webhook"; + +export const updatedProduct = createShopifyWebhookTrigger({ + name: "updated_product", + description: "Triggered when a product is updated.", + topic: "products/update", + displayName: "Updated Product", + sampleData: { + "id": 8282295566587, + "title": "My AP Product", + "body_html": "Well this is nice", + "vendor": "Kofahi", + "product_type": "Test", + "created_at": "2024-01-02T20:36:40+03:00", + "handle": "my-ap-product-1", + "updated_at": "2024-01-02T20:36:40+03:00", + "published_at": "2024-01-02T20:36:40+03:00", + "template_suffix": null, + "published_scope": "global", + "tags": "", + "status": "active", + "variants": [ + { + "id": 45134980382971, + "product_id": 8282295566587, + "title": "Default Title", + "price": "0.000", + "sku": "", + "position": 1, + "inventory_policy": "deny", + "compare_at_price": null, + "fulfillment_service": "manual", + "inventory_management": null, + "option1": "Default Title", + "option2": null, + "option3": null, + "created_at": "2024-01-02T20:36:40+03:00", + "updated_at": "2024-01-02T20:36:40+03:00", + "taxable": true, + "barcode": null, + "grams": 0, + "image_id": null, + "weight": 0, + "weight_unit": "kg", + "inventory_item_id": 47200507134203, + "inventory_quantity": 0, + "old_inventory_quantity": 0, + "requires_shipping": true + } + ], + "options": [ + { + "id": 10640607805691, + "product_id": 8282295566587, + "name": "Title", + "position": 1, + "values": [ + "Default Title" + ] + } + ], + "images": [ + { + "id": 41679215657211, + "product_id": 8282295566587, + "position": 1, + "created_at": "2024-01-02T20:36:40+03:00", + "updated_at": "2024-01-02T20:36:40+03:00", + "alt": null, + "width": 500, + "height": 500, + "src": "https://cdn.shopify.com/s/files/1/0676/6598/5787/products/416a37a613b156f9d23e4fa1fd5358d7.png?v=1704217000", + "variant_ids": [] + } + ], + "image": { + "id": 41679215657211, + "product_id": 8282295566587, + "position": 1, + "created_at": "2024-01-02T20:36:40+03:00", + "updated_at": "2024-01-02T20:36:40+03:00", + "alt": null, + "width": 500, + "height": 500, + "src": "https://cdn.shopify.com/s/files/1/0676/6598/5787/products/416a37a613b156f9d23e4fa1fd5358d7.png?v=1704217000", + "variant_ids": [] + } + } +}); \ No newline at end of file diff --git a/packages/pieces/text-helper/package.json b/packages/pieces/text-helper/package.json index a8807ab44..278d855fc 100644 --- a/packages/pieces/text-helper/package.json +++ b/packages/pieces/text-helper/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-text-helper", - "version": "0.0.3" -} + "version": "0.1.2" +} \ No newline at end of file diff --git a/packages/pieces/text-helper/src/index.ts b/packages/pieces/text-helper/src/index.ts index c79af4dfd..f3740a954 100644 --- a/packages/pieces/text-helper/src/index.ts +++ b/packages/pieces/text-helper/src/index.ts @@ -10,7 +10,7 @@ export const textHelper = createPiece({ displayName : 'Text Helper', auth : PieceAuth.None(), logoUrl : 'https://cdn.activepieces.com/pieces/text-helper.svg', - authors : ['abaza738','joeworkman'], + authors : ['abaza738','joeworkman','AbdulTheActivePiecer'], actions : [concat, replace, split, find, markdownToHTML, htmlToMarkdown], triggers : [], }); diff --git a/packages/pieces/text-helper/src/lib/actions/replace.ts b/packages/pieces/text-helper/src/lib/actions/replace.ts index f35dd9684..20f103640 100644 --- a/packages/pieces/text-helper/src/lib/actions/replace.ts +++ b/packages/pieces/text-helper/src/lib/actions/replace.ts @@ -23,8 +23,21 @@ export const replace = createAction({ required: false, description:'Leave empty to delete found results.', }), + replaceOnlyFirst: Property.Checkbox({ + displayName: 'Replace Only First Match', + required: false, + description:'Only replaces the first instance of the search value.', + }) }, run: async (ctx) => { + + if(ctx.propsValue.replaceOnlyFirst){ + const expression = RegExp(ctx.propsValue.searchValue); + return ctx.propsValue.text.replace( + expression, + ctx.propsValue.replaceValue || '' + ); + } const expression = RegExp(ctx.propsValue.searchValue,'g'); return ctx.propsValue.text.replaceAll( expression, diff --git a/packages/pieces/vbout/.eslintrc.json b/packages/pieces/vbout/.eslintrc.json new file mode 100644 index 000000000..7cd4bf646 --- /dev/null +++ b/packages/pieces/vbout/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts", + "*.tsx", + "*.js", + "*.jsx" + ], + "rules": {} + }, + { + "files": [ + "*.ts", + "*.tsx" + ], + "rules": {} + }, + { + "files": [ + "*.js", + "*.jsx" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/packages/pieces/vbout/README.md b/packages/pieces/vbout/README.md new file mode 100644 index 000000000..85915d9f8 --- /dev/null +++ b/packages/pieces/vbout/README.md @@ -0,0 +1,7 @@ +# pieces-vbout + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-vbout` to build the library. diff --git a/packages/pieces/vbout/package.json b/packages/pieces/vbout/package.json new file mode 100644 index 000000000..300df9198 --- /dev/null +++ b/packages/pieces/vbout/package.json @@ -0,0 +1,4 @@ +{ + "name": "@activepieces/piece-vbout", + "version": "0.0.1" +} diff --git a/packages/pieces/vbout/project.json b/packages/pieces/vbout/project.json new file mode 100644 index 000000000..daaaf1d20 --- /dev/null +++ b/packages/pieces/vbout/project.json @@ -0,0 +1,43 @@ +{ + "name": "pieces-vbout", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/vbout/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/vbout", + "tsConfig": "packages/pieces/vbout/tsconfig.lib.json", + "packageJson": "packages/pieces/vbout/package.json", + "main": "packages/pieces/vbout/src/index.ts", + "assets": [ + "packages/pieces/vbout/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-vbout {args.ver} {args.tag}", + "dependsOn": [ + "build" + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ], + "options": { + "lintFilePatterns": [ + "packages/pieces/vbout/**/*.ts" + ] + } + } + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/pieces/vbout/src/index.ts b/packages/pieces/vbout/src/index.ts new file mode 100644 index 000000000..638043efe --- /dev/null +++ b/packages/pieces/vbout/src/index.ts @@ -0,0 +1,61 @@ +import { createPiece, PieceAuth } from '@activepieces/pieces-framework'; +import { addContactAction } from './lib/actions/add-contact'; +import { addTagToContactAction } from './lib/actions/add-tag-to-contact'; +import { createEmailMarketingCampaignAction } from './lib/actions/create-campaign'; +import { createEmailListAction } from './lib/actions/create-email-list'; +import { createSocialMediaMessageAction } from './lib/actions/create-social-media-message'; +import { getContactByEmailAction } from './lib/actions/get-contact-by-email'; +import { getEmailListAction } from './lib/actions/get-email-list'; +import { removeTagFromContactAction } from './lib/actions/remove-tag-from-contact'; +import { unsubscribeContactAction } from './lib/actions/unsubscribe-contact'; +import { updateContactAction } from './lib/actions/update-contact'; +import { makeClient } from './lib/common'; + +const markdown = ` +To obtain your API key, follow these steps: + +1.Go to **settings** by clicking your profile-pic (top-right).\n +2.Navigate to **API Integrations** section.\n +3.Under **API USER KEY** ,copy API key.\n +`; + +export const vboutAuth = PieceAuth.SecretText({ + displayName: 'API Key', + required: true, + description: markdown, + async validate({ auth }) { + const client = makeClient(auth as string); + try { + await client.validateAuth(); + return { + valid: true, + }; + } catch (e) { + return { + valid: false, + error: 'Invalid API key.', + }; + } + }, +}); + +export const vbout = createPiece({ + displayName: 'VBOUT', + auth: vboutAuth, + minimumSupportedRelease: '0.9.0', + logoUrl: 'https://cdn.activepieces.com/pieces/vbout.png', + authors: ['kishanprmr'], + actions: [ + addContactAction, + addTagToContactAction, + createEmailListAction, + createEmailMarketingCampaignAction, + createSocialMediaMessageAction, + getContactByEmailAction, + getEmailListAction, + removeTagFromContactAction, + unsubscribeContactAction, + updateContactAction, + ], + triggers: [], +}); diff --git a/packages/pieces/vbout/src/lib/actions/add-contact.ts b/packages/pieces/vbout/src/lib/actions/add-contact.ts new file mode 100644 index 000000000..acce58fad --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/add-contact.ts @@ -0,0 +1,27 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient, vboutCommon } from '../common'; + +export const addContactAction = createAction({ + auth: vboutAuth, + name: 'vbout_add_contact', + displayName: 'Add Contact to List', + description: 'Adds a contact to a given email list.', + props: { + email: Property.ShortText({ + displayName: 'Email Address', + required: true, + }), + listid: vboutCommon.listid(true), + status: vboutCommon.contactStatus(true), + ipaddress: Property.ShortText({ + displayName: 'IP Address', + required: false, + }), + fields: vboutCommon.listFields, + }, + async run(context) { + const client = makeClient(context.auth as string); + return await client.addContact(context.propsValue); + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/add-tag-to-contact.ts b/packages/pieces/vbout/src/lib/actions/add-tag-to-contact.ts new file mode 100644 index 000000000..279620075 --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/add-tag-to-contact.ts @@ -0,0 +1,28 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient } from '../common'; + +export const addTagToContactAction = createAction({ + auth: vboutAuth, + name: 'vbout_add_tag', + displayName: 'Add Tag to Contact', + description: 'Adds the tag to the contact.', + props: { + email: Property.ShortText({ + displayName: 'Email Address', + required: true, + }), + tagname: Property.Array({ + displayName: 'Tag Name', + required: true, + }), + }, + async run(context) { + const { email, tagname } = context.propsValue; + const client = makeClient(context.auth as string); + return await client.addTagToContact({ + email: email, + tagname: tagname as string[], + }); + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/create-campaign.ts b/packages/pieces/vbout/src/lib/actions/create-campaign.ts new file mode 100644 index 000000000..e73744b1c --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/create-campaign.ts @@ -0,0 +1,94 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient } from '../common'; + +export const createEmailMarketingCampaignAction = createAction({ + auth: vboutAuth, + name: 'vbout_add_email_marketing_campaign', + displayName: 'Create Email Marketing Campaign', + description: 'Creates a new email campaign for specific list.', + props: { + lists: Property.MultiSelectDropdown({ + displayName: 'Campaign Recipients List', + required: true, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient(auth as string); + const res = await client.listEmailLists(); + return { + disabled: false, + options: res.lists.items.map((list) => { + return { + label: list.name, + value: list.id, + }; + }), + }; + }, + }), + name: Property.ShortText({ + displayName: 'Campaign Name', + required: true, + }), + subject: Property.ShortText({ + displayName: 'Campaign Subject', + required: true, + }), + type: Property.StaticDropdown({ + displayName: 'Campaign Type', + required: true, + defaultValue: 'standard', + options: { + disabled: false, + options: [ + { + label: 'Standard', + value: 'standard', + }, + { + label: 'Automated', + value: 'automated', + }, + ], + }, + }), + fromemail: Property.ShortText({ + displayName: 'From Email', + required: true, + }), + from_name: Property.ShortText({ + displayName: 'From Name', + required: true, + }), + reply_to: Property.ShortText({ + displayName: 'Reply To', + required: true, + }), + body: Property.LongText({ + displayName: 'Message Body', + required: true, + }), + }, + async run(context) { + const { lists, name, from_name, fromemail, reply_to, subject, body, type } = + context.propsValue; + const client = makeClient(context.auth as string); + return await client.addCampaign({ + lists: lists.join(','), + name, + from_name, + fromemail, + reply_to, + subject, + body, + type, + }); + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/create-email-list.ts b/packages/pieces/vbout/src/lib/actions/create-email-list.ts new file mode 100644 index 000000000..85fdf0bd7 --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/create-email-list.ts @@ -0,0 +1,71 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient } from '../common'; + +export const createEmailListAction = createAction({ + auth: vboutAuth, + name: 'vbout_create_email_list', + displayName: 'Create Email List', + description: 'Creates a new email list.', + props: { + name: Property.ShortText({ + required: true, + displayName: 'Name', + description: 'The name of the list.', + }), + email_subject: Property.LongText({ + required: false, + displayName: 'Email Subject', + description: 'The default subscription subject.', + }), + reply_to: Property.ShortText({ + required: false, + displayName: 'Reply To', + description: 'The Reply to email of the list.', + }), + fromemail: Property.ShortText({ + required: false, + displayName: 'From Email', + description: 'The From email of the list.', + }), + from_name: Property.ShortText({ + required: false, + displayName: 'From Name', + description: 'The From name of the list.', + }), + notify_email: Property.ShortText({ + required: false, + displayName: 'Notify Email', + description: 'Notification Email.', + }), + success_email: Property.ShortText({ + required: false, + displayName: 'Success Email', + description: 'Subscription Success Email.', + }), + success_message: Property.ShortText({ + required: false, + displayName: 'Success Message', + description: 'Subscription Success Message.', + }), + error_message: Property.ShortText({ + required: false, + displayName: 'Error Message', + description: 'Subscription Error Message.', + }), + confirmation_email: Property.ShortText({ + required: false, + displayName: 'Confirmation Email', + description: 'Confirmation Email Message.', + }), + confirmation_message: Property.ShortText({ + required: false, + displayName: 'Confirmation Message', + }), + }, + + async run({ auth, propsValue }) { + const client = makeClient(auth as string); + return await client.createEmailList(propsValue); + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/create-social-media-message.ts b/packages/pieces/vbout/src/lib/actions/create-social-media-message.ts new file mode 100644 index 000000000..04365ed42 --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/create-social-media-message.ts @@ -0,0 +1,21 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../../'; +import { makeClient, vboutCommon } from '../common'; +export const createSocialMediaMessageAction = createAction({ + auth: vboutAuth, + name: 'vbout_create_social_media_message', + displayName: 'Create Social Media Message', + description: 'Post a message to one of your social media channel.', + props: { + message: Property.LongText({ + displayName: 'Message', + required: true, + }), + channel: vboutCommon.socialMediaChannel, + channelid: vboutCommon.socialMediaProfile, + }, + async run(context) { + const client = makeClient(context.auth as string); + return await client.createSocialMediaPost(context.propsValue); + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/get-contact-by-email.ts b/packages/pieces/vbout/src/lib/actions/get-contact-by-email.ts new file mode 100644 index 000000000..e5b907a25 --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/get-contact-by-email.ts @@ -0,0 +1,23 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient, vboutCommon } from '../common'; + +export const getContactByEmailAction = createAction({ + auth: vboutAuth, + name: 'vbout_get_contact_by_email', + displayName: 'Get Contact by Email', + description: 'Retrives the contact by email.', + props: { + listid: vboutCommon.listid(false), + email: Property.ShortText({ + displayName: 'Email', + required: true, + }), + }, + async run({ auth, propsValue }) { + const client = makeClient(auth as string); + const { listid, email } = propsValue; + + return await client.getContactByEmail(email, listid); + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/get-email-list.ts b/packages/pieces/vbout/src/lib/actions/get-email-list.ts new file mode 100644 index 000000000..b7306d47a --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/get-email-list.ts @@ -0,0 +1,19 @@ +import { createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient, vboutCommon } from '../common'; + +export const getEmailListAction = createAction({ + auth: vboutAuth, + name: 'vbout_get_email_list', + displayName: 'Get List Details with Custom Fields', + description: 'Retrives specific list details with custom fields.', + props: { + listid: vboutCommon.listid(true), + }, + async run({ auth, propsValue }) { + const client = makeClient(auth as string); + const listId = propsValue.listid!; + + return await client.getEmailList(listId); + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/remove-tag-from-contact.ts b/packages/pieces/vbout/src/lib/actions/remove-tag-from-contact.ts new file mode 100644 index 000000000..37a250558 --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/remove-tag-from-contact.ts @@ -0,0 +1,26 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient } from '../common'; + +export const removeTagFromContactAction = createAction({ + auth: vboutAuth, + name: 'vbout_remove_tag', + displayName: 'Remove Tags from Contact', + description: 'Removes tags from an existing contact.', + props: { + email: Property.ShortText({ + displayName: 'Email Address', + required: true, + }), + tagname: Property.ShortText({ + displayName: 'Tag Name', + required: true, + description: `use comma for multiple tag e.g. **tag1,tag2**.`, + }), + }, + async run(context) { + const { email, tagname } = context.propsValue; + const client = makeClient(context.auth as string); + return await client.removeTagFromContact(email, tagname); + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/unsubscribe-contact.ts b/packages/pieces/vbout/src/lib/actions/unsubscribe-contact.ts new file mode 100644 index 000000000..f7e30234d --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/unsubscribe-contact.ts @@ -0,0 +1,38 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient, vboutCommon } from '../common'; +import { ContactStatusValues } from '../common/models'; + +export const unsubscribeContactAction = createAction({ + auth: vboutAuth, + name: 'vbout_unsubscribe_contact', + displayName: 'Unsubscribe Contact', + description: 'Unsubscribes an existing contact in a given email list.', + props: { + email: Property.ShortText({ + displayName: 'Contact Email', + required: true, + description: 'Contact email for update.', + }), + listid: vboutCommon.listid(true), + }, + async run(context) { + const client = makeClient(context.auth as string); + const { email, listid } = context.propsValue; + const res = await client.getContactByEmail( + email as string, + listid as string + ); + const contact = res.response.data.contact; + + if ('errorCode' in contact) { + return res; + } else { + const contactId = contact[0].id; + return await client.updateContact({ + id: contactId, + status: ContactStatusValues.UNSUBSCRIBE, + }); + } + }, +}); diff --git a/packages/pieces/vbout/src/lib/actions/update-contact.ts b/packages/pieces/vbout/src/lib/actions/update-contact.ts new file mode 100644 index 000000000..b00f472f5 --- /dev/null +++ b/packages/pieces/vbout/src/lib/actions/update-contact.ts @@ -0,0 +1,40 @@ +import { Property, createAction } from '@activepieces/pieces-framework'; +import { vboutAuth } from '../..'; +import { makeClient, vboutCommon } from '../common'; + +export const updateContactAction = createAction({ + auth: vboutAuth, + name: 'vbout_update_contact', + displayName: 'Update Contact', + description: 'Updates a contact in a given email list.', + props: { + email: Property.ShortText({ + displayName: 'Contact Email', + required: true, + description: 'Contact email for update.', + }), + listid: vboutCommon.listid(true), + status: vboutCommon.contactStatus(false), + ipaddress: Property.ShortText({ + displayName: 'IP Address', + required: false, + }), + fields: vboutCommon.listFields, + }, + async run(context) { + const client = makeClient(context.auth as string); + const { email } = context.propsValue; + const res = await client.getContactByEmail(email as string); + const contact = res.response.data.contact; + + if ('errorCode' in contact) { + return res; + } else { + const contactId = contact[0].id; + return await client.updateContact({ + id: contactId, + ...context.propsValue, + }); + } + }, +}); diff --git a/packages/pieces/vbout/src/lib/common/client.ts b/packages/pieces/vbout/src/lib/common/client.ts new file mode 100644 index 000000000..c5956ec75 --- /dev/null +++ b/packages/pieces/vbout/src/lib/common/client.ts @@ -0,0 +1,170 @@ +import { + HttpMessageBody, + HttpMethod, + QueryParams, + httpClient, +} from '@activepieces/pieces-common'; +import { vboutCommon } from '.'; +import { + CampaignCreateRequest, + ContactCreateRequest, + ContactList, + ContactUpdateRequest, + EmailListCreateRequest, + SocialMediaChannelListResponse, + SocialMediaPostCreateRequest, + TagCreateRequest, + VboutResponseBody, +} from './models'; + +function emptyValueFilter( + accessor: (key: string) => any +): (key: string) => boolean { + return (key: string) => { + const val = accessor(key); + return ( + val !== null && + val !== undefined && + (typeof val != 'string' || val.length > 0) + ); + }; +} + +export function prepareQuery(request?: Record): QueryParams { + const params: QueryParams = {}; + if (!request) return params; + Object.keys(request) + .filter(emptyValueFilter((k) => request[k])) + .forEach((k: string) => { + params[k] = (request as Record)[k].toString(); + }); + return params; +} +export class VboutClient { + constructor(private apiKey: string) {} + + async makeRequest( + method: HttpMethod, + url: string, + query?: QueryParams, + body?: object + ): Promise { + const res = await httpClient.sendRequest({ + method, + url: vboutCommon.baseUrl + url, + queryParams: { key: this.apiKey, ...query }, + body, + }); + return res.body; + } + + async validateAuth() { + return await this.makeRequest(HttpMethod.GET, '/app/me'); + } + + async listEmailLists() { + return ( + await this.makeRequest< + VboutResponseBody<{ + lists: { + count: number; + items: ContactList[]; + }; + }> + >(HttpMethod.GET, '/emailmarketing/getlists') + ).response.data; + } + + async getContactByEmail(email: string, listId?: string) { + return await this.makeRequest< + VboutResponseBody<{ + contact: { + id: string; + email: string; + listid: string; + list_name: string; + [key: string]: any; + }[]; + }> + >( + HttpMethod.GET, + '/emailmarketing/getcontactbyemail', + prepareQuery({ email: email, listid: listId }) + ); + } + + async getEmailList(listId: string) { + return await this.makeRequest>( + HttpMethod.GET, + '/emailmarketing/getlist', + prepareQuery({ id: listId }) + ); + } + async createEmailList(request: EmailListCreateRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/emailMarketing/AddList', + undefined, + request + ); + } + async addContact(request: ContactCreateRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/emailMarketing/AddContact', + undefined, + request + ); + } + async updateContact(request: ContactUpdateRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/emailMarketing/EditContact', + undefined, + request + ); + } + async addTagToContact(request: TagCreateRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/emailMarketing/AddTag', + undefined, + request + ); + } + async removeTagFromContact(email: string, tagname: string) { + return await this.makeRequest( + HttpMethod.DELETE, + '/emailMarketing/RemoveTag', + prepareQuery({ + email, + tagname, + }) + ); + } + + async addCampaign(request: CampaignCreateRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/emailMarketing/AddCampaign', + undefined, + request + ); + } + async listSocialMediaChannels() { + return ( + await this.makeRequest< + VboutResponseBody<{ channels: SocialMediaChannelListResponse }> + >(HttpMethod.GET, '/socialMedia/Channels') + ).response.data; + } + + async createSocialMediaPost(request: SocialMediaPostCreateRequest) { + return await this.makeRequest( + HttpMethod.POST, + '/socialMedia/AddPost', + undefined, + request + ); + } +} diff --git a/packages/pieces/vbout/src/lib/common/index.ts b/packages/pieces/vbout/src/lib/common/index.ts new file mode 100644 index 000000000..7d9b3cf75 --- /dev/null +++ b/packages/pieces/vbout/src/lib/common/index.ts @@ -0,0 +1,171 @@ +import { DynamicPropsValue, Property } from '@activepieces/pieces-framework'; +import { VboutClient } from './client'; +import { + ContactStatusValues, + SocialMediaChannelValues, + SocialMediaProfile, +} from './models'; +export function makeClient(apiKey: string): VboutClient { + return new VboutClient(apiKey); +} + +export const vboutCommon = { + baseUrl: 'https://api.vbout.com/1', + listid: (required = true) => + Property.Dropdown({ + displayName: 'List ID', + required: required, + refreshers: [], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Connect your account first', + options: [], + }; + } + const client = makeClient(auth as string); + const res = await client.listEmailLists(); + return { + disabled: false, + options: res.lists.items.map((list) => { + return { + label: list.name, + value: list.id, + }; + }), + }; + }, + }), + listFields: Property.DynamicProperties({ + displayName: 'Fields', + required: true, + refreshers: ['listid'], + props: async ({ auth, listid }) => { + if (!auth || !listid) { + return { + disabled: true, + options: [], + placeholder: 'Please connect your account and select Email List.', + }; + } + const fields: DynamicPropsValue = {}; + const client = makeClient(auth as unknown as string); + const contactList = await client.getEmailList( + listid as unknown as string + ); + const contactListFields = contactList.response.data.list.fields; + Object.keys(contactListFields).forEach((key) => { + fields[key] = Property.ShortText({ + displayName: contactListFields[key], + required: false, + }); + }); + return fields; + }, + }), + contactStatus: (required = true) => + Property.StaticDropdown({ + displayName: 'Contact Status', + required: required, + options: { + disabled: false, + options: [ + { + label: 'Unconfirmed', + value: ContactStatusValues.UNCONFIRMED, + }, + { + label: 'Active', + value: ContactStatusValues.ACTIVE, + }, + { + label: 'Unsubscribe', + value: ContactStatusValues.UNSUBSCRIBE, + }, + { + label: 'Bounced Email', + value: ContactStatusValues.BOUNCED_EMAIL, + }, + ], + }, + }), + socialMediaChannel: Property.StaticDropdown({ + displayName: 'Social Media Channel', + required: true, + options: { + disabled: false, + options: [ + { + label: 'Twitter', + value: SocialMediaChannelValues.TWITTER, + }, + { + label: 'LinkedIn', + value: SocialMediaChannelValues.LINKEDIN, + }, + { + label: 'Facebook', + value: SocialMediaChannelValues.FACEBOOK, + }, + ], + }, + }), + socialMediaProfile: Property.Dropdown({ + displayName: 'Social Media Account', + required: true, + refreshers: ['channel'], + options: async ({ auth, channel }) => { + if (!auth || !channel) { + return { + disabled: true, + options: [], + placeholder: + 'Please connect your account and select social media channel.', + }; + } + const client = makeClient(auth as string); + const { channels } = await client.listSocialMediaChannels(); + let options: { label: string; value: string }[] = []; + switch (channel as string) { + case SocialMediaChannelValues.TWITTER: { + options = [ + ...options, + ...mapSocialMediaProfile(channels.Twitter.profiles), + ]; + break; + } + case SocialMediaChannelValues.FACEBOOK: { + options = [ + ...options, + ...mapSocialMediaProfile(channels.Facebook.pages), + ]; + break; + } + case SocialMediaChannelValues.LINKEDIN: { + options = [ + ...options, + ...mapSocialMediaProfile(channels.Linkedin.companies), + ...mapSocialMediaProfile(channels.Linkedin.profiles), + ]; + break; + } + } + return { + disabled: false, + options: options, + }; + }, + }), +}; + +function mapSocialMediaProfile( + profiles: SocialMediaProfile[] +): { label: string; value: string }[] { + return profiles.map((profile) => { + return { + label: profile.name, + value: profile.id, + }; + }); +} diff --git a/packages/pieces/vbout/src/lib/common/models.ts b/packages/pieces/vbout/src/lib/common/models.ts new file mode 100644 index 000000000..bf867f18e --- /dev/null +++ b/packages/pieces/vbout/src/lib/common/models.ts @@ -0,0 +1,122 @@ +import { HttpMessageBody } from '@activepieces/pieces-common'; +export interface VboutResponseBody extends HttpMessageBody { + response: { + header: { + status: string; + dataType: string; + limit: string; + cached?: string; + }; + data: T; + }; +} +export interface EmailListCreateRequest { + name: string; + email_subject?: string; + reply_to?: string; + fromemail?: string; + from_name?: string; + doubleOptin?: string; + notify?: string; + notify_email?: string; + success_email?: string; + success_message?: string; + error_message?: string; + confirmation_email?: string; + confirmation_message?: string; + communications?: boolean; +} +export interface ContactList { + id: string; + name: string; + form_title: string; + email_subject: string; + reply_to: string; + from_email: string; + from_name: string; + confimation_email: string; + success_email: string; + confimation_message: string; + success_message: string; + error_message: string; + doubleOptin: string; + notify_email: string; + creation_date: string; + fields: { + [key: string]: string; + }; +} + +export interface ContactCreateRequest { + listid?: string; + status?: string; + email: string; + ipaddress?: string; + fields?: { + [key: string]: string; + }; +} + +export interface ContactUpdateRequest { + id: string; + listid?: string; + status?: string; + email?: string; + ipaddress?: string; + fields?: { + [key: string]: string; + }; +} + +export interface TagCreateRequest { + email: string; + tagname: string[]; +} + +export interface CampaignCreateRequest { + name: string; + subject: string; + fromemail: string; + from_name: string; + reply_to: string; + body: string; + type: string; + lists: string; +} +export interface SocialMediaProfile { + id: string; + name: string; +} +export interface SocialMediaChannelListResponse { + Facebook: { + count: number; + pages: SocialMediaProfile[]; + }; + Twitter: { + count: number; + profiles: SocialMediaProfile[]; + }; + Linkedin: { + count: number; + profiles: SocialMediaProfile[]; + companies: SocialMediaProfile[]; + }; +} + +export interface SocialMediaPostCreateRequest { + message: string; + channel: string; + channelid: string; +} + +export enum SocialMediaChannelValues { + TWITTER = 'twitter', + LINKEDIN = 'linkedin', + FACEBOOK = 'facebook', +} +export enum ContactStatusValues { + UNCONFIRMED = '0', + ACTIVE = '1', + UNSUBSCRIBE = '2', + BOUNCED_EMAIL = '3', +} diff --git a/packages/pieces/vbout/tsconfig.json b/packages/pieces/vbout/tsconfig.json new file mode 100644 index 000000000..f2400abed --- /dev/null +++ b/packages/pieces/vbout/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/pieces/vbout/tsconfig.lib.json b/packages/pieces/vbout/tsconfig.lib.json new file mode 100644 index 000000000..e583571ea --- /dev/null +++ b/packages/pieces/vbout/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/packages/pieces/vtiger/.eslintrc.json b/packages/pieces/vtiger/.eslintrc.json new file mode 100644 index 000000000..3230caf3d --- /dev/null +++ b/packages/pieces/vtiger/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": "error" + } + } + ] +} diff --git a/packages/pieces/vtiger/README.md b/packages/pieces/vtiger/README.md new file mode 100644 index 000000000..e6dff9909 --- /dev/null +++ b/packages/pieces/vtiger/README.md @@ -0,0 +1,7 @@ +# pieces-vtiger + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-vtiger` to build the library. diff --git a/packages/pieces/vtiger/package-lock.json b/packages/pieces/vtiger/package-lock.json new file mode 100644 index 000000000..478da6311 --- /dev/null +++ b/packages/pieces/vtiger/package-lock.json @@ -0,0 +1,55 @@ +{ + "name": "@activepieces/piece-vtiger", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@activepieces/piece-vtiger", + "version": "0.0.1", + "dependencies": { + "@bowbridge/vtiger-js": "^1.17.0" + } + }, + "node_modules/@bowbridge/vtiger-js": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@bowbridge/vtiger-js/-/vtiger-js-1.17.0.tgz", + "integrity": "sha512-NTMxJ3rym5EPF7nidpVVNbghMYAugqKmeP6oCCmEfKBxV35CXuGquiOyXXbZNZfLf8xZfFZ+0rBY7mH5v5ZqMg==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "axios": "^0.24.0" + } + }, + "node_modules/axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "peer": true, + "dependencies": { + "follow-redirects": "^1.14.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "peer": true, + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + } + } +} diff --git a/packages/pieces/vtiger/package.json b/packages/pieces/vtiger/package.json new file mode 100644 index 000000000..af11822d0 --- /dev/null +++ b/packages/pieces/vtiger/package.json @@ -0,0 +1,12 @@ +{ + "name": "@activepieces/piece-vtiger", + "version": "0.1.7", + "dependencies": { + "@activepieces/pieces-framework": "*", + "@activepieces/pieces-common": "*", + "crypto-js": "4.2.0", + "dayjs": "1.11.9", + "lodash": "4.17.21", + "tslib": "2.6.2" + } +} diff --git a/packages/pieces/vtiger/project.json b/packages/pieces/vtiger/project.json new file mode 100644 index 000000000..ded2c0c41 --- /dev/null +++ b/packages/pieces/vtiger/project.json @@ -0,0 +1,44 @@ +{ + "name": "pieces-vtiger", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/vtiger/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/vtiger", + "tsConfig": "packages/pieces/vtiger/tsconfig.lib.json", + "packageJson": "packages/pieces/vtiger/package.json", + "main": "packages/pieces/vtiger/src/index.ts", + "assets": [ + "packages/pieces/vtiger/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-vtiger {args.ver} {args.tag}", + "dependsOn": [ + "build" + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ], + "options": { + "lintFilePatterns": [ + "packages/pieces/vtiger/**/*.ts", + "packages/pieces/vtiger/package.json" + ] + } + } + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/pieces/vtiger/src/index.ts b/packages/pieces/vtiger/src/index.ts new file mode 100644 index 000000000..d85ff9a94 --- /dev/null +++ b/packages/pieces/vtiger/src/index.ts @@ -0,0 +1,111 @@ + +import { createPiece, PieceAuth, Property } from "@activepieces/pieces-framework"; +import { createRecord } from "./lib/actions/create-record"; +import { instanceLogin, isBaseUrl } from "./lib/common"; +import { deleteRecord } from "./lib/actions/delete-record"; +import { getRecord } from "./lib/actions/get-record"; +import { searchRecords } from "./lib/actions/search-record"; +import { updateRecord } from "./lib/actions/update-record"; +import { makeAPICall } from "./lib/actions/make-api-call"; +import { newOrUpdatedRecord } from "./lib/triggers/new-or-updated-record"; + +const markdownProperty = ` +To obtain your Access Key, follow these steps: + +1. Login to Vtiger CRM: +Open a web browser and log in to your Vtiger CRM instance. + +2. Navigate to User Profile: +In the top right corner, click on your profile name. +Select "My Preferences." + +3. The system will generate an access key for you. +Copy and securely store the access key. This key will be used for authentication when making API requests. +Note: + +Access keys are sensitive information, and they should be kept secure. +Treat the access key like a password. Do not share it publicly or expose it in an insecure manner. +`; + + +export const vtigerAuth = PieceAuth.CustomAuth({ + description: markdownProperty, + props: { + instance_url: Property.ShortText({ + displayName: "VTiger Instance URL", + description: "For the instance URL, add the url without the endpoint. For example enter https://.od2.vtiger.com instead of https://.od2.vtiger.com/restapi/v1/vtiger/default", + required: true, + }), + username: Property.ShortText({ + displayName: 'Username', + description: 'Enter your username/email', + required: true + }), + password: PieceAuth.SecretText({ + displayName: 'Access Key', + description: 'Enter your access Key', + required: true + }), + }, + validate: async ({ auth }) => { + const { instance_url, username, password } = auth; + + try { + if (!isBaseUrl(instance_url)) { + return { + valid: false, + error: "Please ensure that the website is valid and does not contain any paths, for example, https://.od2.vtiger.com ", + }; + } + if (instance_url.endsWith("/")) { + return { + valid: false, + error: "Please enter the URL without a trailing slash. E.g. https://.od2.vtiger.com instead of https://.od2.vtiger.com/", + }; + } + if (instance_url.includes("restapi/")) { + return { + valid: false, + error: "Add the url without the endpoint. For example add https://.od2.vtiger.com/ instead of https://.od2.vtiger.com/restapi/v1/vtiger/default", + }; + } + + const instance = await instanceLogin(instance_url, username, password) + if (!instance) { + return { + valid: false, + error: 'Invalid credentials, check and try again.' + } + } + + return { + valid: true + } + } catch (err) { + return { + valid: false, + error: 'Unexpected error. Please check your credentials and try again.' + } + } + }, + required: true, +}) + +export const vtiger = createPiece({ + displayName: "Vtiger", + auth: vtigerAuth, + minimumSupportedRelease: '0.9.0', + logoUrl: "https://cdn.activepieces.com/pieces/vtiger.png", + authors: ['kanarelo'], + actions: [ + createRecord, + getRecord, + updateRecord, + deleteRecord, + searchRecords, + makeAPICall + ], + triggers: [ + newOrUpdatedRecord + ], +}); diff --git a/packages/pieces/vtiger/src/lib/actions/create-record.ts b/packages/pieces/vtiger/src/lib/actions/create-record.ts new file mode 100644 index 000000000..17e9a0db0 --- /dev/null +++ b/packages/pieces/vtiger/src/lib/actions/create-record.ts @@ -0,0 +1,50 @@ +import { HttpMethod, httpClient } from "@activepieces/pieces-common"; +import { createAction } from "@activepieces/pieces-framework"; +import { vtigerAuth } from "../.."; +import { instanceLogin, recordProperty } from "../common"; +import { elementTypeProperty } from "../common"; + +//Docs: https://code.vtiger.com/vtiger/vtigercrm-manual/-/wikis/Webservice-Docs +//Extra: https://help.vtiger.com/article/147111249-Rest-API-Manual + + +export const createRecord = createAction({ + name: 'create_record', + auth: vtigerAuth, + displayName: 'Create Record', + description: 'Create a Record', + props: { + elementType: elementTypeProperty, + record: recordProperty() + }, + async run({ propsValue: { elementType, record }, auth }) { + const instance = await instanceLogin(auth.instance_url, auth.username, auth.password) + + if (instance !== null) { + const response = await httpClient.sendRequest[]>({ + method: HttpMethod.POST, + url: `${auth.instance_url}/webservice.php`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: { + operation: 'create', + sessionName: instance.sessionId ?? instance.sessionName, + elementType: elementType, + element: JSON.stringify(record) + } + }); + + console.debug({ + operation: 'create', + sessionName: instance.sessionId ?? instance.sessionName, + elementType: elementType, + element: JSON.stringify(record) + }) + + return response.body + } + + return null + } +}); diff --git a/packages/pieces/vtiger/src/lib/actions/delete-record.ts b/packages/pieces/vtiger/src/lib/actions/delete-record.ts new file mode 100644 index 000000000..7f0b394fa --- /dev/null +++ b/packages/pieces/vtiger/src/lib/actions/delete-record.ts @@ -0,0 +1,43 @@ +import { HttpMethod, httpClient } from "@activepieces/pieces-common"; +import { createAction } from "@activepieces/pieces-framework"; +import { vtigerAuth } from "../.."; +import { instanceLogin, recordIdProperty } from "../common"; +import { elementTypeProperty } from "../common"; + +//Docs: https://code.vtiger.com/vtiger/vtigercrm-manual/-/wikis/Webservice-Docs +//Extra: https://help.vtiger.com/article/147111249-Rest-API-Manual + + +export const deleteRecord = createAction({ + name: 'delete_record', + auth: vtigerAuth, + displayName: 'Delete Record', + description: 'Delete a Record', + props: { + elementType: elementTypeProperty, + record: recordIdProperty() + }, + async run({ propsValue: { elementType, record }, auth: { instance_url, username, password } }) { + const instance = await instanceLogin(instance_url, username, password) + + if (instance !== null) { + const response = await httpClient.sendRequest[]>({ + method: HttpMethod.POST, + url: `${instance_url}/webservice.php`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: { + operation: 'delete', + sessionName: instance.sessionId ?? instance.sessionName, + elementType, + id: record['id'] + } + }); + + return response.body + } + + return null + } +}); diff --git a/packages/pieces/vtiger/src/lib/actions/get-record.ts b/packages/pieces/vtiger/src/lib/actions/get-record.ts new file mode 100644 index 000000000..f86c6292c --- /dev/null +++ b/packages/pieces/vtiger/src/lib/actions/get-record.ts @@ -0,0 +1,39 @@ +import { HttpMethod, httpClient } from "@activepieces/pieces-common"; +import { createAction } from "@activepieces/pieces-framework"; +import { vtigerAuth } from "../.."; +import { instanceLogin, recordIdProperty } from "../common"; +import { elementTypeProperty } from "../common"; + +//Docs: https://code.vtiger.com/vtiger/vtigercrm-manual/-/wikis/Webservice-Docs +//Extra: https://help.vtiger.com/article/147111249-Rest-API-Manual + +export const getRecord = createAction({ + name: 'get_record', + auth: vtigerAuth, + displayName: 'Get Record', + description: 'Get a Record by value', + props: { + elementType: elementTypeProperty, + record: recordIdProperty() + }, + async run({ propsValue: {elementType, record}, auth: {instance_url, username, password} }) { + const instance = await instanceLogin(instance_url, username, password) + + if (instance !== null) { + const response = await httpClient.sendRequest[]>({ + method: HttpMethod.GET, + url: `${instance_url}/webservice.php`, + queryParams: { + operation: 'retrieve', + sessionName: instance.sessionId ?? instance.sessionName, + elementType: elementType as unknown as string, + ...record + } + }); + + return response.body + } + + return null + } +}); diff --git a/packages/pieces/vtiger/src/lib/actions/make-api-call.ts b/packages/pieces/vtiger/src/lib/actions/make-api-call.ts new file mode 100644 index 000000000..bb98e73d5 --- /dev/null +++ b/packages/pieces/vtiger/src/lib/actions/make-api-call.ts @@ -0,0 +1,69 @@ +import { Property, createAction } from "@activepieces/pieces-framework"; +import { vtigerAuth } from "../.."; +import { instanceLogin } from "../common"; +import { HttpMessageBody, HttpMethod, HttpRequest, httpClient } from "@activepieces/pieces-common"; + +//Docs: https://code.vtiger.com/vtiger/vtigercrm-manual/-/wikis/Webservice-Docs +//Extra: https://help.vtiger.com/article/147111249-Rest-API-Manual + +export const makeAPICall = createAction({ + name: 'make_api_call', + auth: vtigerAuth, + displayName: 'Make API Call', + description: 'Performs an arbitrary authorized API call. ', + props: { + method: Property.StaticDropdown({ + displayName: "Http Method", + description: "Select the HTTP method you want to use", + required: true, + options: { + options: [ + { label: "GET", value: HttpMethod.GET }, + { label: "POST", value: HttpMethod.POST }, + ] + } + }), + headers: Property.Json({ + displayName: "Headers", + description: `Enter the desired request headers. Skip the authorization headers`, + required: true, + defaultValue: {} + }), + data: Property.Json({ + displayName: "Data", + description: `Enter the data to pass. if its POST, it will be sent as body data, and if GET, as query string`, + required: true, + defaultValue: {} + }) + }, + async run({ propsValue, auth }) { + const vtigerInstance = await instanceLogin(auth.instance_url, auth.username, auth.password) + if (vtigerInstance === null) return + + const data: Record = { + sessionName: vtigerInstance.sessionId ?? vtigerInstance.sessionName, + ...(propsValue.data ?? {}) + } + + const httpRequest: HttpRequest = { + url: `${auth.instance_url}/webservice.php`, + method: propsValue.method ?? HttpMethod.GET, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + ...(propsValue.headers ?? {}) + } + } + httpRequest[propsValue.method === HttpMethod.GET ? 'queryParams' : 'body'] = data; + + const response = await httpClient + .sendRequest[]>(httpRequest); + + if ([200, 201].includes(response.status)) { + return response.body + } + + return { + error: "Unexpected outcome!" + } + } +}); \ No newline at end of file diff --git a/packages/pieces/vtiger/src/lib/actions/search-record.ts b/packages/pieces/vtiger/src/lib/actions/search-record.ts new file mode 100644 index 000000000..9f9dac2bb --- /dev/null +++ b/packages/pieces/vtiger/src/lib/actions/search-record.ts @@ -0,0 +1,98 @@ +import { DynamicPropsValue, Property, createAction } from "@activepieces/pieces-framework"; +import { vtigerAuth } from "../.."; +import { Operation, instanceLogin, prepareHttpRequest } from "../common"; +import { httpClient } from "@activepieces/pieces-common"; + + +//Docs: https://code.vtiger.com/vtiger/vtigercrm-manual/-/wikis/Webservice-Docs +//Extra: https://help.vtiger.com/article/147111249-Rest-API-Manual + +export const searchRecords = createAction({ + name: 'search_records', + auth: vtigerAuth, + displayName: 'Search Records', + description: 'Search for a record.', + props: { + search_by: Property.StaticDropdown({ + displayName: "Search By", + description: `Select the mode of search for your records`, + required: true, + defaultValue: 'filter', + options: { + options: [ + {label: "Filter", value: 'filter'}, + {label: "Query", value: 'query'}, + ] + } + }), + search: Property.DynamicProperties({ + displayName: "Search Fields", + description: "Add new fields to be created in the new record", + required: true, + refreshers: ['search_by'], + props: async ({ auth, search_by }) => { + if (!auth || !search_by) { + return {}; + } + + const fields: DynamicPropsValue = {} + + if ((search_by as unknown as string) === "filter") { + fields['filter'] = Property.DynamicProperties({ + displayName: "filter", + description: `Enter your filter criteria`, + required: true, + refreshers: ['search_by'], + props: async ({ search_by }) => { + console.debug("search_by", search_by) + + return { + simple: Property.LongText({ + displayName: "query", + description: `Enter the query to search for record new record`, + required: true + }) + } as DynamicPropsValue + } + }) + } else { + fields['query'] = Property.LongText({ + displayName: "query", + description: `Enter the query to search for record new record`, + required: true + }) + } + + return fields; + } + }), + limit: Property.Number({ + displayName: "Limit", + description: "Enter the maximum number of records to return.", + defaultValue: 100, + required: true + }) + }, + async run({ propsValue, auth }) { + const vtigerInstance = await instanceLogin(auth.instance_url, auth.username, auth.password) + if (vtigerInstance === null) return + + const httpRequest = prepareHttpRequest( + auth.instance_url, + vtigerInstance.sessionId ?? vtigerInstance.sessionName, + "query" as Operation, + {} + ); + + const response = await httpClient + .sendRequest[]>(httpRequest); + + if ([200, 201].includes(response.status)) { + return response.body + } + + return { + error: "Unexpected outcome!" + } + } +}); \ No newline at end of file diff --git a/packages/pieces/vtiger/src/lib/actions/update-record.ts b/packages/pieces/vtiger/src/lib/actions/update-record.ts new file mode 100644 index 000000000..6c0e728df --- /dev/null +++ b/packages/pieces/vtiger/src/lib/actions/update-record.ts @@ -0,0 +1,240 @@ +import { HttpMethod, httpClient } from "@activepieces/pieces-common"; +import { DropdownState, DynamicPropsValue, PiecePropValueSchema, Property, createAction } from "@activepieces/pieces-framework"; +import { vtigerAuth } from "../.."; +import { instanceLogin, VTigerAuthValue, Modules, Field, getRecordReference } from "../common"; +import { elementTypeProperty } from "../common"; + +function sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export const updateRecord = createAction({ + name: 'update_record', + auth: vtigerAuth, + displayName: 'Update Record', + description: 'Update a Record', + props: { + elementType: elementTypeProperty, + id: Property.Dropdown({ + displayName: 'Id', + description: 'The record\'s id', + required: true, + refreshers: ["elementType"], + options: async ({ auth, elementType}) => { + if (!auth || !elementType) { + return { + disabled: true, + options: [], + placeholder: 'Please select the element type and setup authentication to continue.' + } + } + + let c = 0 + let instance = null + while (!instance && c < 3) { + instance = await instanceLogin( + (auth as VTigerAuthValue).instance_url, + (auth as VTigerAuthValue).username, + (auth as VTigerAuthValue).password + ) + await sleep(1500); + c++ + } + + if (!instance) { + return { + disabled: true, + options: [], + placeholder: 'Authentication failed.' + } + } + + const response = await httpClient.sendRequest<{ + success: boolean, + result: Record[] + }>({ + method: HttpMethod.GET, + url: `${(auth as VTigerAuthValue)['instance_url']}/webservice.php`, + queryParams: { + sessionName: instance.sessionId ?? instance.sessionName, + operation: "query", + elementType: elementType as unknown as string, + query: `SELECT * FROM ${elementType} LIMIT 100;` + } + }) + + if (!response.body.success) + return { + disabled: true, + options: [], + placeholder: 'Request unsuccessful.' + } + + const element: string = (elementType as unknown as string) + + return { + options: response.body.result.map( + (r) => ({ label: Modules[element](r), value: r['id'] }) + ), + disabled: false, + }; + }, + }), + record: Property.DynamicProperties({ + displayName: "Record Fields", + description: "Add new fields to be created in the new record", + required: true, + refreshers: ['id', 'elementType'], + props: async ({ auth, id, elementType }) => { + if (!auth || !elementType) { + return {}; + } + + const instance = await instanceLogin( + auth['instance_url'], + auth['username'], + auth['password'] + ) + if (!instance) return {} + + let defaultValue: Record + if (id && 'id') { + const retrieve_response = await httpClient.sendRequest<{ success: boolean, result: Record}>({ + method: HttpMethod.GET, + url: `${auth['instance_url']}/webservice.php`, + queryParams: { + operation: 'retrieve', + sessionName: instance.sessionId ?? instance.sessionName, + elementType: elementType as unknown as string, + id: id as unknown as string + } + }); + if (retrieve_response.body.result) { + defaultValue = retrieve_response.body.result + } else { + defaultValue = {} + } + } else { + defaultValue = {} + } + + const describe_response = await httpClient.sendRequest<{ + success: boolean, + result: { fields: Field[] } + }>({ + method: HttpMethod.GET, + url: `${auth['instance_url']}/webservice.php`, + queryParams: { + sessionName: instance.sessionId ?? instance.sessionName, + operation: "describe", + elementType: elementType as unknown as string + } + }); + + const fields: DynamicPropsValue = {} + + if (describe_response.body.success) { + const generateField = async (field: Field) => { + const params = { + displayName: field.label, + description: `Field ${field.name} of object type ${elementType}`, + required: field.mandatory + } + + if ( + ['string', 'text', 'mediumtext', 'phone', 'url', 'email'].includes(field.type.name) + ) { + if (['mediumtext', 'url'].includes(field.type.name)) { + fields[field.name] = Property.LongText({...params, defaultValue: defaultValue?.[field.name] as string}) + } else { + fields[field.name] = Property.ShortText({...params, defaultValue: defaultValue?.[field.name] as string}) + } + } else if (['picklist', 'reference', 'owner'].includes(field.type.name)) { + + let options: DropdownState + if (field.type.name === 'picklist') { + options = { disabled: false, options: (field.type.picklistValues ?? []) } + } else if (field.type.name === "owner") { + options = await getRecordReference(auth as PiecePropValueSchema, ["Users"]) + } else if (field.type.refersTo) { + options = await getRecordReference(auth as PiecePropValueSchema, field.type.refersTo ?? []) + } else { + options = { disabled: false, options: [] } + } + + fields[field.name] = Property.StaticDropdown({ + ...params, + defaultValue: defaultValue?.[field.name] as string, + options + }) + } else if (['double' ,'integer', 'currency'].includes(field.type.name)) { + fields[field.name] = Property.Number({ + ...params, + defaultValue: defaultValue?.[field.name] as number + }) + } else if (['boolean'].includes(field.type.name)) { + fields[field.name] = Property.Checkbox({ + displayName: field.label, + description: `The fields to fill in the object type ${elementType}`, + required: field.mandatory, + defaultValue: defaultValue?.[field.name] as boolean + }) + } else if (['date', 'datetime', 'time'].includes(field.type.name)) { + fields[field.name] = Property.DateTime({ + displayName: field.label, + description: `The fields to fill in the object type ${elementType}`, + defaultValue: defaultValue?.[field.name] as string, + required: field.mandatory + }) + } + } + + for (const field of describe_response.body.result.fields) { + if (field.name === "id") + continue + + await generateField(field) + } + } + + return fields; + } + }) + }, + async run({ propsValue: { elementType, id, record }, auth }) { + const instance = await instanceLogin(auth.instance_url, auth.username, auth.password) + + if (instance !== null) { + const response = await httpClient.sendRequest[]>({ + method: HttpMethod.POST, + url: `${auth.instance_url}/webservice.php`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: { + operation: 'update', + sessionName: instance.sessionId ?? instance.sessionName, + elementType: elementType, + element: JSON.stringify({ + id: id, + ...record + }) + } + }); + + console.debug({ + operation: 'update', + sessionName: instance.sessionId ?? instance.sessionName, + elementType: elementType, + element: JSON.stringify({ + id: id, + ...record + }) + }) + + return response.body + } + + return null + } +}); diff --git a/packages/pieces/vtiger/src/lib/common.ts b/packages/pieces/vtiger/src/lib/common.ts new file mode 100644 index 000000000..e5225e95d --- /dev/null +++ b/packages/pieces/vtiger/src/lib/common.ts @@ -0,0 +1,457 @@ +import { HttpHeaders, HttpMessageBody, HttpMethod, HttpRequest, httpClient } from '@activepieces/pieces-common'; +import { DropdownState, DynamicPropsValue, PiecePropValueSchema, Property } from '@activepieces/pieces-framework'; +import * as crypto from 'crypto-js'; +import { Challenge, Instance } from './models'; +import { vtigerAuth } from '..'; + + +export const isBaseUrl = (urlString: string): boolean => { + try { + const url = new URL(urlString); + return !url.pathname || url.pathname === '/'; + } catch (error) { + // Handle invalid URLs here, e.g., return false or throw an error + return false; + } +} + +export const md5 = (contents: string) => crypto.MD5(contents).toString(); +export const calculateAuthKey = (challengeToken: string, accessKey: string): string => + crypto.MD5(challengeToken + accessKey).toString(crypto.enc.Hex); + +export const instanceLogin = async (instance_url: string, username: string, password: string, debug = false) => { + const endpoint = `${instance_url}/webservice.php` + const challenge = (await httpClient.sendRequest<{success: boolean, result: Challenge}>({ + method: HttpMethod.GET, + url: `${endpoint}?operation=getchallenge&username=${username}`, + })); + + const accessKey = calculateAuthKey(challenge.body.result.token, password) + const response = await httpClient.sendRequest<{success: boolean, result: Instance}>({ + method: HttpMethod.POST, + url: `${endpoint}`, + headers: { + 'Content-Type': 'multipart/form-data' + }, + body: { + operation: 'login', + username, + accessKey + } + }) + + if (debug) { + console.debug(">>>>>>>>>>>> LOGIN", response.body, { + method: HttpMethod.POST, + url: `${endpoint}`, + headers: { + 'Content-Type': 'multipart/form-data' + }, + body: { + operation: 'login', + username, + accessKey + } + }) + } + if (response.body.success) { + return response.body.result; + } + + return null; +} + +export type Operation = "create" | "retrieve" | "delete" | "update" | "query" | "listtypes" + +export const Operations: Record = { + "listtypes": { + method: HttpMethod.GET, + }, + "create": { + method: HttpMethod.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }, + "retrieve": { + method: HttpMethod.GET + }, + "delete": { + method: HttpMethod.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }, + "update": { + method: HttpMethod.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }, + "query": { + method: HttpMethod.GET, + } +} + +export const prepareHttpRequest = ( + instanceUrl: string, + sessionName: string, + operation: Operation, + record: Record +) => { + const data: Record = { + operation, + sessionName, + ...record + } + if ('element' in record) + data['element'] = JSON.stringify(record['element']) + + const httpRequest: HttpRequest = { + url: `${instanceUrl}/webservice.php`, + method: Operations[operation].method, + headers: Operations[operation].headers, + } + + if (Operations[operation].method === HttpMethod.GET) { + httpRequest['queryParams'] = data + } else if (Operations[operation].method === HttpMethod.POST) { + httpRequest['body'] = data + } + + return httpRequest +} + +interface BodyParams { + method: HttpMethod + headers?: HttpHeaders +} + +export const Modules: Record = { + "Accounts": (record: Record) => `${record['accountname']}`, + "Assets": (record: Record) => `${record['assetname']}`, + "CompanyDetails": (record: Record) => `${record['organizationname']}`, + "Contacts": (record: Record) => `${record['email']}`, + "Currency": (record: Record) => `${record['currency_name']}`, + "DocumentFolders": (record: Record) => `${record['foldername']}`, + "Documents": (record: Record) => `${record['notes_title']}`, + "Emails": (record: Record) => `${record['subject']}`, + "Events": (record: Record) => `${record['subject']}`, + "Faq": (record: Record) => `${record['faq_no']}`, + "Groups": (record: Record) => `${record['groupname']}`, + "HelpDesk": (record: Record) => `${record['ticket_no']}`, + "Invoice": (record: Record) => `${record['invoice_no']}`, + "Leads": (record: Record) => `${record['lead_no']}: ${record['firstname']} ${record['lastname']}`, + "LineItem": (record: Record) => `${record['productid']}`, + "ModComments": (record: Record) => `${record['commentcontent']}`, + "Potentials": (record: Record) => `${record['potentialname']}`, + "PriceBooks": (record: Record) => `${record['bookname']}`, + "Products": (record: Record) => `${record['productname']}`, + "ProductTaxes": (record: Record) => `#${record['taxid']} pid: ${record['productid']}`, + "Project": (record: Record) => `${record['projectname']}`, + "ProjectMilestone": (record: Record) => `${record['projectmilestonename']}`, + "ProjectTask": (record: Record) => `${record['projecttaskname']}`, + "PurchaseOrder": (record: Record) => `${record['subject']}`, + "Quotes": (record: Record) => `${record['subject']}`, + "SalesOrder": (record: Record) => `${record['salesorder_no']}`, + "ServiceContracts": (record: Record) => `${record['subject']}`, + "Services": (record: Record) => `${record['servicename']}`, + "SLA": (record: Record) => `${record['policy_name']}`, + "Tax": (record: Record) => `${record['taxname']}`, + "Users": (record: Record) => `${record['user_name']}`, + "Vendors": (record: Record) => `${record['vendorname']}`, +} + +export const elementTypeProperty = Property.StaticDropdown({ + displayName: "Module Type", + description: "The module / element type", + required: true, + options: { + options: [ + {label: "Accounts", value: "Accounts"}, + {label: "Assets", value: "Assets"}, + {label: "CompanyDetails", value: "CompanyDetails"}, + {label: "Contacts", value: "Contacts"}, + {label: "Currency", value: "Currency"}, + {label: "DocumentFolders", value: "DocumentFolders"}, + {label: "Documents", value: "Documents"}, + {label: "Emails", value: "Emails"}, + {label: "Events", value: "Events"}, + {label: "Faq", value: "Faq"}, + {label: "Groups", value: "Groups"}, + {label: "HelpDesk", value: "HelpDesk"}, + {label: "Invoice", value: "Invoice"}, + {label: "Leads", value: "Leads"}, + {label: "LineItem", value: "LineItem"}, + {label: "ModComments", value: "ModComments"}, + {label: "Potentials", value: "Potentials"}, + {label: "PriceBooks", value: "PriceBooks"}, + {label: "Products", value: "Products"}, + {label: "ProductTaxes", value: "ProductTaxes"}, + {label: "Project", value: "Project"}, + {label: "ProjectMilestone", value: "ProjectMilestone"}, + {label: "ProjectTask", value: "ProjectTask"}, + {label: "PurchaseOrder", value: "PurchaseOrder"}, + {label: "Quotes", value: "Quotes"}, + {label: "SalesOrder", value: "SalesOrder"}, + {label: "ServiceContracts", value: "ServiceContracts"}, + {label: "Services", value: "Services"}, + {label: "SLA", value: "SLA"}, + {label: "Tax", value: "Tax"}, + {label: "Users", value: "Users"}, + {label: "Vendors", value: "Vendors"}, + ] + } +}) + +export interface Field { + name: string + dblabel: string + label: string + default: string + mandatory: boolean + type: { + name: string + length?: string + refersTo?: string[] + picklistValues?: { + label: string + value: string + }[] + } +} + +export type VTigerAuthValue = PiecePropValueSchema + +export const recordIdProperty = () => Property.DynamicProperties({ + displayName: "Record Fields", + description: "Add new fields to be created in the new record", + required: true, + refreshers: ['elementType'], + props: async ({ auth, elementType }) => { + if (!auth || !elementType) { + return {}; + } + + const instance = await instanceLogin( + auth['instance_url'], + auth['username'], + auth['password'] + ) + if (!instance) return {} + + const response = await httpClient.sendRequest<{ + success: boolean, + result: Record[] + }>({ + method: HttpMethod.GET, + url: `${(auth as VTigerAuthValue)['instance_url']}/webservice.php`, + queryParams: { + sessionName: instance.sessionId ?? instance.sessionName, + operation: "query", + elementType: elementType as unknown as string, + query: `SELECT * FROM ${elementType} LIMIT 100;` + } + }) + + if (!response.body.success) return {} + + const fields: DynamicPropsValue = {} + const _type: string = (elementType as unknown as string) + const _module: CallableFunction = Modules[_type] + + fields["id"] = Property.StaticDropdown({ + displayName: 'Id', + description: 'The record\'s id', + required: true, + options: { + options: response.body.result.map((r) => ({ + label: _module(r), + value: r['id'] + })) + } + }) + + return fields + } +}) + +export const FieldMapping = { + autogenerated: Property.ShortText, + string: Property.ShortText, + text: Property.ShortText, + double: Property.Number, + integer: Property.Number, + mediumtext : Property.LongText, + phone : Property.LongText, + url: Property.LongText, + email: Property.LongText, + picklist: Property.StaticDropdown, + reference: Property.StaticDropdown, + currency: Property.Number, + boolean: Property.Checkbox, + owner: Property.StaticDropdown, + date: Property.DateTime, + datetime: Property.DateTime, + file: Property.File, + time: Property.DateTime +} + +export async function getRecordReference(auth: PiecePropValueSchema, modules: string[]): Promise> { + const module = modules[0]; //Limit to the first reference for now + const vtigerInstance = await instanceLogin(auth['instance_url'], auth['username'], auth['password']) + if (vtigerInstance === null) + return { + disabled: true, + options: [], + }; + + const httpRequest = prepareHttpRequest( + auth['instance_url'], + vtigerInstance.sessionId ?? vtigerInstance.sessionName, + "query" as Operation, + { query: `SELECT * FROM ${module};` } + ); + + const response = await httpClient.sendRequest<{success: boolean, result: Record[]}>(httpRequest); + + if (response.body.success) { + return { + disabled: false, + options: response.body.result.map( + (record) => { + return { + label: Modules[module](record), + value: record['id'] as string + } + }) + } + } + + return { + disabled: true, + options: [], + } +} + +export const recordProperty = (create=true) => Property.DynamicProperties({ + displayName: "Record Fields", + description: "Add new fields to be created in the new record", + required: true, + refreshers: ['id', 'elementType'], + props: async ({ auth, id, elementType }) => { + if (!auth || !elementType) { + return {}; + } + + const instance = await instanceLogin( + auth['instance_url'], + auth['username'], + auth['password'] + ) + if (!instance) return {} + + let defaultValue: Record + + if (create) { + defaultValue = {} + } else { + if (id && 'id' in id) { + const retrieve_response = await httpClient.sendRequest>({ + method: HttpMethod.GET, + url: `${auth['instance_url']}/webservice.php`, + queryParams: { + operation: 'retrieve', + sessionName: instance.sessionId ?? instance.sessionName, + elementType: elementType as unknown as string, + id: id['id'] as unknown as string + } + }); + defaultValue = retrieve_response.body + } else { + defaultValue = {} + } + } + + const describe_response = await httpClient.sendRequest<{ + success: boolean, + result: { fields: Field[] } + }>({ + method: HttpMethod.GET, + url: `${auth['instance_url']}/webservice.php`, + queryParams: { + sessionName: instance.sessionId ?? instance.sessionName, + operation: "describe", + elementType: elementType as unknown as string + } + }); + + const fields: DynamicPropsValue = {} + + if (describe_response.body.success) { + const generateField = async (field: Field) => { + const params = { + displayName: field.label, + description: `Field ${field.name} of object type ${elementType}`, + required: field.mandatory + } + + if ( + ['string', 'text', 'mediumtext', 'phone', 'url', 'email'].includes(field.type.name) + ) { + if (['mediumtext', 'url'].includes(field.type.name)) { + fields[field.name] = Property.LongText({...params, defaultValue: defaultValue?.[field.name] as string}) + } else { + fields[field.name] = Property.ShortText({...params, defaultValue: defaultValue?.[field.name] as string}) + } + } else if (['picklist', 'reference', 'owner'].includes(field.type.name)) { + + let options: DropdownState + if (field.type.name === 'picklist') { + options = { disabled: false, options: (field.type.picklistValues ?? []) } + } else if (field.type.name === "owner") { + options = await getRecordReference(auth as PiecePropValueSchema, ["Users"]) + } else if (field.type.refersTo) { + options = await getRecordReference(auth as PiecePropValueSchema, field.type.refersTo ?? []) + } else { + options = { disabled: false, options: [] } + } + + fields[field.name] = Property.StaticDropdown({ + ...params, + defaultValue: defaultValue?.[field.name] as string, + options + }) + } else if (['double' ,'integer', 'currency'].includes(field.type.name)) { + fields[field.name] = Property.Number({ + ...params, + defaultValue: defaultValue?.[field.name] as number + }) + } else if (['boolean'].includes(field.type.name)) { + fields[field.name] = Property.Checkbox({ + displayName: field.label, + description: `The fields to fill in the object type ${elementType}`, + required: field.mandatory, + defaultValue: defaultValue?.[field.name] as boolean + }) + } else if (['date', 'datetime', 'time'].includes(field.type.name)) { + fields[field.name] = Property.DateTime({ + displayName: field.label, + description: `The fields to fill in the object type ${elementType}`, + defaultValue: defaultValue?.[field.name] as string, + required: field.mandatory + }) + } + } + + for (const field of describe_response.body.result.fields) { + if (field.name === "id") + continue + + await generateField(field) + } + } + + return fields; + } +}) \ No newline at end of file diff --git a/packages/pieces/vtiger/src/lib/models.ts b/packages/pieces/vtiger/src/lib/models.ts new file mode 100644 index 000000000..d5f3ba52c --- /dev/null +++ b/packages/pieces/vtiger/src/lib/models.ts @@ -0,0 +1,23 @@ +export interface Challenge { + token: string, // Challenge token to be used for login. + serverTime: string, // Current Server time + expireTime: string +} + +export interface Instance { + sessionId: string + sessionName: string + userId: string + version: string + vtigerVersion: string + username: string + first_name: string + last_name: string + email: string + time_zone: string + hour_format: string + date_format: string + is_admin: string + call_duration: string + other_event_duration: string +} diff --git a/packages/pieces/vtiger/src/lib/triggers/new-or-updated-record.ts b/packages/pieces/vtiger/src/lib/triggers/new-or-updated-record.ts new file mode 100644 index 000000000..47850a2b0 --- /dev/null +++ b/packages/pieces/vtiger/src/lib/triggers/new-or-updated-record.ts @@ -0,0 +1,124 @@ +import { DedupeStrategy, Polling, httpClient, pollingHelper } from '@activepieces/pieces-common'; +import { createTrigger, PiecePropValueSchema, Property, TriggerStrategy } from '@activepieces/pieces-framework'; +import { vtigerAuth } from "../.."; +import { elementTypeProperty, instanceLogin, prepareHttpRequest } from '../common'; +import dayjs from 'dayjs'; +import { filter, sortBy } from 'lodash'; + +export const newOrUpdatedRecord = createTrigger({ + auth: vtigerAuth, + name: 'new_or_updated_record', + displayName: 'New or Updated Record', + description: 'Triggers when a new record is introduced or a record is updated.', + props: { + elementType: elementTypeProperty, + watchBy: Property.StaticDropdown({ + displayName: "Watch By", + description: "Column to watch for trigger", + required: true, + options: { + options: [ + { value: "createdtime", label: "Created Time" }, + { value: "modifiedtime", label: "Modified Time" }, + ] + } + }), + limit: Property.Number({ + displayName: "Limit", + description: "Enter the maximum number of records to return.", + defaultValue: 100, + required: true + }) + }, + sampleData: { + "success": true, + "result": [ + { + "id": "3x291", + "createdtime": "2020-07-22 12:46:55", + "modifiedtime": "2020-07-22 12:46:55" + } + ] + }, + type: TriggerStrategy.POLLING, + async test(ctx) { + return await pollingHelper.test(polling, { ...ctx }) + }, + async onEnable(ctx) { + await pollingHelper.onEnable(polling, { ...ctx }) + }, + async onDisable(ctx) { + await pollingHelper.onDisable(polling, { ...ctx }) + }, + async run(ctx) { + return await pollingHelper.poll(polling, { ...ctx }) + } +}); + + +const polling: Polling, { elementType?: string, watchBy?: string, limit?: number }> = { + strategy: DedupeStrategy.TIMEBASED, + items: async ({ auth, propsValue, lastFetchEpochMS }) => { + const items = await fetchRecords({ auth, propsValue, lastFetchEpochMS }) + + return (items ?? []).map((item) => { + return { + epochMilliSeconds: dayjs( + propsValue.watchBy === "createdtime" ? item['createdtime'] : item['modifiedtime'] + ).valueOf(), + data: item, + } + }); + } +} + +const fetchRecords = async ( + { auth, propsValue, lastFetchEpochMS }: + { auth: Record, propsValue: Record, lastFetchEpochMS: number } +) => { + const vtigerInstance = await instanceLogin( + auth['instance_url'], + auth['username'], + auth['password'] + ) + if (vtigerInstance === null) { + return [] + } + + const query = `SELECT * FROM ${propsValue['elementType']} ;`; + + const httpRequest = prepareHttpRequest( + auth['instance_url'], + vtigerInstance.sessionId ?? vtigerInstance.sessionName, + "query", + { query } + ); + const response = await httpClient.sendRequest<{ + success: boolean + result: Record[] + }>(httpRequest); + + if (response.body.success) { + const lastFetch = dayjs(lastFetchEpochMS) + const records = response.body.result + const limit = propsValue['limit'] as number + + const newOrUpdatedRecords = filter( + records, (record) => { + const watchTime = dayjs(record[propsValue['watchBy'] as string] ?? 0); + return watchTime.diff(lastFetch) >= 0; + }) + const sortedRecords = sortBy( + newOrUpdatedRecords, + (record) => record[propsValue['watchBy'] as string] + ) + + if (limit > 0) { + return sortedRecords.slice(0, limit) + } else { + return sortedRecords + } + } + + return [] +} \ No newline at end of file diff --git a/packages/pieces/vtiger/tsconfig.json b/packages/pieces/vtiger/tsconfig.json new file mode 100644 index 000000000..f2400abed --- /dev/null +++ b/packages/pieces/vtiger/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/pieces/vtiger/tsconfig.lib.json b/packages/pieces/vtiger/tsconfig.lib.json new file mode 100644 index 000000000..e583571ea --- /dev/null +++ b/packages/pieces/vtiger/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/packages/shared/package.json b/packages/shared/package.json index f6aa1c3e8..188a9db36 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,5 +1,5 @@ { "name": "@activepieces/shared", - "version": "0.10.47", + "version": "0.10.58", "type": "commonjs" -} \ No newline at end of file +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 8194b8bfe..63449a0ef 100755 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -34,10 +34,11 @@ export { StoreEntry, StoreEntryId } from './lib/store-entry/store-entry' export * from './lib/user' export { TestFlowRunRequestBody } from './lib/flow-run/test-flow-run-request' export { Trigger, EmptyTrigger, PieceTriggerSettings, PieceTrigger, WebhookTrigger, TriggerType, AUTHENTICATION_PROPERTY_NAME } from './lib/flows/triggers/trigger' -export { FlowVersion, FlowVersionState, FlowVersionId } from './lib/flows/flow-version' +export { FlowVersion, FlowVersionState, FlowVersionId, FlowVersionMetadata } from './lib/flows/flow-version' +export { Flow, FlowId } from './lib/flows/flow' export * from './lib/file' export * from './lib/flows/flow-helper' -export { FlowRun, FlowRunId, RunEnvironment, RunTerminationReason } from './lib/flow-run/flow-run' +export { FlowRun, FlowRunId, RunEnvironment, RunTerminationReason, FlowRetryStrategy, FlowRetryPayload } from './lib/flow-run/flow-run' export { Project, ProjectId, ProjectType } from './lib/project/project' export * from './lib/flows/dto/create-flow-request' export { SeekPage, Cursor } from './lib/common/seek-page' @@ -53,6 +54,7 @@ export * from './lib/flows' export * from './lib/flows/dto/list-flows-request' export * from './lib/project/project' import { TypeSystem } from '@sinclair/typebox/system' +export { RetryFlowRequestBody } from './lib/flow-run/test-flow-run-request' export * from './lib/flows/dto/flow-template-request' export * from './lib/flows/dto/update-flow-status-request' // Look at https://github.com/sinclairzx81/typebox/issues/350 diff --git a/packages/shared/src/lib/app-connection/app-connection.ts b/packages/shared/src/lib/app-connection/app-connection.ts index 4631ff54d..54b2c8ed2 100755 --- a/packages/shared/src/lib/app-connection/app-connection.ts +++ b/packages/shared/src/lib/app-connection/app-connection.ts @@ -77,7 +77,7 @@ export type AppConnectionValue export type AppConnection = BaseModel & { name: string type: Type - appName: string + pieceName: string projectId: string status: AppConnectionStatus value: AppConnectionValue @@ -94,7 +94,7 @@ export const AppConnectionWithoutSensitiveData = Type.Object({ ...BaseModelSchema, name: Type.String(), type: Type.Enum(AppConnectionType), - appName: Type.String(), + pieceName: Type.String(), projectId: ApId, status: Type.Enum(AppConnectionStatus), }, { diff --git a/packages/shared/src/lib/app-connection/dto/read-app-connection-request.ts b/packages/shared/src/lib/app-connection/dto/read-app-connection-request.ts index 3de7cc122..97e32b5e0 100755 --- a/packages/shared/src/lib/app-connection/dto/read-app-connection-request.ts +++ b/packages/shared/src/lib/app-connection/dto/read-app-connection-request.ts @@ -3,7 +3,7 @@ import { Static, Type } from '@sinclair/typebox' export const ListAppConnectionsRequestQuery = Type.Object({ cursor: Type.Optional(Type.String({})), projectId: Type.String(), - appName: Type.Optional(Type.String({})), + pieceName: Type.Optional(Type.String({})), limit: Type.Optional(Type.Number({})), }) export type ListAppConnectionsRequestQuery = Static diff --git a/packages/shared/src/lib/app-connection/dto/upsert-app-connection-request.ts b/packages/shared/src/lib/app-connection/dto/upsert-app-connection-request.ts index f2b4937cf..16a4c4c4e 100644 --- a/packages/shared/src/lib/app-connection/dto/upsert-app-connection-request.ts +++ b/packages/shared/src/lib/app-connection/dto/upsert-app-connection-request.ts @@ -4,7 +4,7 @@ import { OAuth2AuthorizationMethod } from '../oauth2-authorization-method' const commonAuthProps = { name: Type.String({}), - appName: Type.String({}), + pieceName: Type.String({}), projectId: Type.String({}), } diff --git a/packages/shared/src/lib/authentication/model/principal-type.ts b/packages/shared/src/lib/authentication/model/principal-type.ts index c9e98af48..d7f982053 100755 --- a/packages/shared/src/lib/authentication/model/principal-type.ts +++ b/packages/shared/src/lib/authentication/model/principal-type.ts @@ -3,7 +3,8 @@ export enum PrincipalType { WORKER = 'WORKER', SERVICE = 'SERVICE', UNKNOWN = 'UNKNOWN', - EXTERNAL = 'EXTERNAL' + EXTERNAL = 'EXTERNAL', + SUPER_USER = 'SUPER_USER', } export const ALL_PRINICPAL_TYPES = Object.values(PrincipalType) @@ -16,4 +17,4 @@ export enum EndpointScope { export enum PlatformRole { OWNER = 'OWNER', MEMBER = 'MEMBER', -} \ No newline at end of file +} diff --git a/packages/shared/src/lib/common/activepieces-error.ts b/packages/shared/src/lib/common/activepieces-error.ts index a45dbb88e..cb21980f6 100755 --- a/packages/shared/src/lib/common/activepieces-error.ts +++ b/packages/shared/src/lib/common/activepieces-error.ts @@ -13,6 +13,7 @@ export class ActivepiecesError extends Error { type ErrorParams = | AppConnectionNotFoundErrorParams + | AuthenticationParams | AuthorizationErrorParams | PermissionDeniedErrorParams | ConfigNotFoundErrorParams @@ -25,7 +26,6 @@ type ErrorParams = | FlowNotFoundErrorParams | FlowOperationErrorParams | FlowRunNotFoundErrorParams - | FlowVersionNotFoundErrorParams | InvalidApiKeyParams | InvalidAppConnectionParams | InvalidBearerTokenParams @@ -33,6 +33,8 @@ type ErrorParams = | InvalidCloudClaimParams | InvalidCredentialsErrorParams | InvalidJwtTokenErrorParams + | InvalidOtpParams + | InvitationOnlySignUpParams | JobRemovalFailureErrorParams | OpenAiFailedErrorParams | PauseMetadataMissingErrorParams @@ -52,6 +54,9 @@ type ErrorParams = | TriggerFailedErrorParams | ValidationErrorParams | InvitationOnlySignUpParams + | UserIsInActiveErrorParams + | DomainIsNotAllowedErrorParams + | EmailAuthIsDisabledParams export type BaseErrorParams = { code: T; @@ -59,12 +64,12 @@ export type BaseErrorParams = { }; export type InvitationOnlySignUpParams = BaseErrorParams< -ErrorCode.INVITATIION_ONLY_SIGN_UP, +ErrorCode.INVITATION_ONLY_SIGN_UP, Record > export type InvalidClaimParams = BaseErrorParams -export type InvalidCloudClaimParams = BaseErrorParams +export type InvalidCloudClaimParams = BaseErrorParams export type InvalidBearerTokenParams = BaseErrorParams< ErrorCode.INVALID_BEARER_TOKEN, @@ -78,6 +83,8 @@ export type FileNotFoundErrorParams = BaseErrorParams< { id: FileId } >; +export type EmailAuthIsDisabledParams = BaseErrorParams> + export type AppConnectionNotFoundErrorParams = BaseErrorParams< ErrorCode.APP_CONNECTION_NOT_FOUND, { @@ -122,18 +129,18 @@ export type FlowRunNotFoundErrorParams = BaseErrorParams< } >; -export type FlowVersionNotFoundErrorParams = BaseErrorParams< - ErrorCode.FLOW_VERSION_NOT_FOUND, - { - id: FlowVersionId; - } ->; - export type InvalidCredentialsErrorParams = BaseErrorParams< ErrorCode.INVALID_CREDENTIALS, null > +export type DomainIsNotAllowedErrorParams = BaseErrorParams< +ErrorCode.DOMAIN_NOT_ALLOWED, +{ + domain: string +} +> + export type EmailIsNotVerifiedErrorParams = BaseErrorParams< ErrorCode.EMAIL_IS_NOT_VERIFIED, { @@ -141,6 +148,13 @@ ErrorCode.EMAIL_IS_NOT_VERIFIED, } > +export type UserIsInActiveErrorParams = BaseErrorParams< +ErrorCode.USER_IS_INACTIVE, +{ + email: string +} +> + export type ExistingUserErrorParams = BaseErrorParams< ErrorCode.EXISTING_USER, { @@ -310,23 +324,32 @@ ErrorCode.SIGN_UP_DISABLED, Record > +export type AuthenticationParams = BaseErrorParams< +ErrorCode.AUTHENTICATION, +{ + message: string +} +> + export type InvalidOtpParams = BaseErrorParams> export enum ErrorCode { APP_CONNECTION_NOT_FOUND = 'APP_CONNECTION_NOT_FOUND', + AUTHENTICATION = 'AUTHENTICATION', AUTHORIZATION = 'AUTHORIZATION', CONFIG_NOT_FOUND = 'CONFIG_NOT_FOUND', + DOMAIN_NOT_ALLOWED = 'DOMAIN_NOT_ALLOWED', EMAIL_IS_NOT_VERIFIED = 'EMAIL_IS_NOT_VERIFIED', ENGINE_OPERATION_FAILURE = 'ENGINE_OPERATION_FAILURE', ENTITY_NOT_FOUND = 'ENTITY_NOT_FOUND', EXECUTION_TIMEOUT = 'EXECUTION_TIMEOUT', + EMAIL_AUTH_DISABLED = 'EMAIL_AUTH_DISABLED', EXISTING_USER = 'EXISTING_USER', FILE_NOT_FOUND = 'FILE_NOT_FOUND', FLOW_INSTANCE_NOT_FOUND = 'INSTANCE_NOT_FOUND', FLOW_NOT_FOUND = 'FLOW_NOT_FOUND', FLOW_OPERATION_INVALID = 'FLOW_OPERATION_INVALID', FLOW_RUN_NOT_FOUND = 'FLOW_RUN_NOT_FOUND', - FLOW_VERSION_NOT_FOUND = 'FLOW_VERSION_NOT_FOUND', INVALID_API_KEY = 'INVALID_API_KEY', INVALID_APP_CONNECTION = 'INVALID_APP_CONNECTION', INVALID_BEARER_TOKEN = 'INVALID_BEARER_TOKEN', @@ -335,7 +358,7 @@ export enum ErrorCode { INVALID_CREDENTIALS = 'INVALID_CREDENTIALS', INVALID_OR_EXPIRED_JWT_TOKEN = 'INVALID_OR_EXPIRED_JWT_TOKEN', INVALID_OTP = 'INVALID_OTP', - INVITATIION_ONLY_SIGN_UP = 'INVITATIION_ONLY_SIGN_UP', + INVITATION_ONLY_SIGN_UP = 'INVITATION_ONLY_SIGN_UP', JOB_REMOVAL_FAILURE = 'JOB_REMOVAL_FAILURE', OPEN_AI_FAILED = 'OPEN_AI_FAILED', PAUSE_METADATA_MISSING = 'PAUSE_METADATA_MISSING', @@ -353,6 +376,7 @@ export enum ErrorCode { USER_NOT_FOUND = 'USER_NOT_FOUND', TRIGGER_ENABLE = 'TRIGGER_ENABLE', TRIGGER_FAILED = 'TRIGGER_FAILED', + USER_IS_INACTIVE = 'USER_IS_INACTIVE', VALIDATION = 'VALIDATION', // TOOKEY: custom errors diff --git a/packages/shared/src/lib/common/index.ts b/packages/shared/src/lib/common/index.ts index a4cecad5c..22079e181 100644 --- a/packages/shared/src/lib/common/index.ts +++ b/packages/shared/src/lib/common/index.ts @@ -1,2 +1,3 @@ export * from './utils' export * from './base-model' +export * from './locale' \ No newline at end of file diff --git a/packages/shared/src/lib/common/locale.ts b/packages/shared/src/lib/common/locale.ts new file mode 100644 index 000000000..942d63539 --- /dev/null +++ b/packages/shared/src/lib/common/locale.ts @@ -0,0 +1,14 @@ +export enum LocalesEnum { + DUTCH = 'nl', + ENGLISH = 'en', + GERMAN = 'de', + ITALIAN = 'it', + FRENCH = 'fr', + HUNGARIAN = 'hu', + SPANISH = 'es', + JAPANESE = 'ja', + INDONESIAN = 'id', + VIETNAMESE = 'vi', + CHINESE_SIMPLIFIED = 'zh', + PORTUGUESE = 'pt', +} \ No newline at end of file diff --git a/packages/shared/src/lib/common/utils/object-utils.ts b/packages/shared/src/lib/common/utils/object-utils.ts index 32fc200de..ef53f0a5d 100644 --- a/packages/shared/src/lib/common/utils/object-utils.ts +++ b/packages/shared/src/lib/common/utils/object-utils.ts @@ -75,9 +75,8 @@ export async function applyFunctionToValues(obj: unknown, apply: (str: unknow return await apply(obj) as T } -const isObject = (obj: unknown): obj is Record => { - return typeof obj === 'object' +export const isObject = (obj: unknown): obj is Record => { + return typeof obj === 'object' && obj !== null && !Array.isArray(obj) } -export type MakeKeyNonNullableAndRequired = T & { [P in K]-?: NonNullable } - +export type MakeKeyNonNullableAndRequired = T & { [P in K]-?: NonNullable } \ No newline at end of file diff --git a/packages/shared/src/lib/common/utils/utils.ts b/packages/shared/src/lib/common/utils/utils.ts index 8e0d2d780..36d53a505 100644 --- a/packages/shared/src/lib/common/utils/utils.ts +++ b/packages/shared/src/lib/common/utils/utils.ts @@ -33,18 +33,3 @@ export function pickBy>( return result }, {}) } - - -//create a generic error class -export class UnhandledSwitchCaseError extends Error { - constructor( - public readonly value: never, - ) { - super( - `Unhandled switch case. Value: ${JSON.stringify( - value, - )}`, - ) - } - -} diff --git a/packages/shared/src/lib/engine/engine-operation.ts b/packages/shared/src/lib/engine/engine-operation.ts index cb1c6f0fc..e6c2124c9 100644 --- a/packages/shared/src/lib/engine/engine-operation.ts +++ b/packages/shared/src/lib/engine/engine-operation.ts @@ -24,7 +24,7 @@ export enum TriggerHookType { } export type EngineOperation = - | ExcuteStepOperation + | ExecuteStepOperation | ExecuteFlowOperation | ExecutePropsOptions | ExecuteTriggerOperation @@ -44,7 +44,7 @@ export type ExecuteValidateAuthOperation = BaseEngineOperation & { export type ExecuteExtractPieceMetadata = PiecePackage & { projectId: string } -export type ExcuteStepOperation = BaseEngineOperation & { +export type ExecuteStepOperation = BaseEngineOperation & { stepName: string flowVersion: FlowVersion } @@ -59,16 +59,16 @@ export type ExecutePropsOptions = BaseEngineOperation & { type BaseExecuteFlowOperation = BaseEngineOperation & { flowVersion: FlowVersion flowRunId: FlowRunId - triggerPayload: unknown executionType: T } export type BeginExecuteFlowOperation = BaseExecuteFlowOperation & { - executionState?: ExecutionState + triggerPayload: unknown } export type ResumeExecuteFlowOperation = BaseExecuteFlowOperation & { executionState: ExecutionState + tasks: number resumePayload: unknown } diff --git a/packages/shared/src/lib/flag/flag.ts b/packages/shared/src/lib/flag/flag.ts index 40f2ebce7..fb8a380f9 100755 --- a/packages/shared/src/lib/flag/flag.ts +++ b/packages/shared/src/lib/flag/flag.ts @@ -23,6 +23,7 @@ export enum ApFlagId { BILLING_ENABLED = 'BILLING_ENABLED', CLOUD_AUTH_ENABLED = 'CLOUD_AUTH_ENABLED', OWN_AUTH2_ENABLED = 'OWN_AUTH2_ENABLED', + EMAIL_AUTH_ENABLED = 'EMAIL_AUTH_ENABLED', CURRENT_VERSION = 'CURRENT_VERSION', EDITION = 'EDITION', ENVIRONMENT = 'ENVIRONMENT', @@ -45,6 +46,7 @@ export enum ApFlagId { TEMPLATES_PROJECT_ID = 'TEMPLATES_PROJECT_ID', THEME = 'THEME', USER_CREATED = 'USER_CREATED', + SHOW_GIT_SYNC = 'SHOW_GIT_SYNC', WEBHOOK_URL_PREFIX = 'WEBHOOK_URL_PREFIX', SHOW_POWERED_BY_AP = 'SHOW_POWERED_BY_AP', PRIVACY_POLICY_URL = 'PRIVACY_POLICY_URL', diff --git a/packages/shared/src/lib/flow-run/flow-run.ts b/packages/shared/src/lib/flow-run/flow-run.ts index 7fb95752c..d7e6a0d78 100755 --- a/packages/shared/src/lib/flow-run/flow-run.ts +++ b/packages/shared/src/lib/flow-run/flow-run.ts @@ -38,3 +38,12 @@ export enum RunEnvironment { PRODUCTION = 'PRODUCTION', TESTING = 'TESTING', } + +export enum FlowRetryStrategy { + ON_LATEST_VERSION = 'ON_LATEST_VERSION', + FROM_FAILED_STEP = 'FROM_FAILED_STEP', +} + +export type FlowRetryPayload = { + strategy: FlowRetryStrategy +} \ No newline at end of file diff --git a/packages/shared/src/lib/flow-run/test-flow-run-request.ts b/packages/shared/src/lib/flow-run/test-flow-run-request.ts index 74b6da945..d8d9c9695 100755 --- a/packages/shared/src/lib/flow-run/test-flow-run-request.ts +++ b/packages/shared/src/lib/flow-run/test-flow-run-request.ts @@ -1,8 +1,15 @@ import { Static, Type } from '@sinclair/typebox' import { ApId } from '../common/id-generator' +import { FlowRetryStrategy } from './flow-run' export const TestFlowRunRequestBody = Type.Object({ flowVersionId: ApId, }) export type TestFlowRunRequestBody = Static + +export const RetryFlowRequestBody = Type.Object({ + strategy: Type.Enum(FlowRetryStrategy), +}) + +export type RetryFlowRequestBody = Static \ No newline at end of file diff --git a/packages/shared/src/lib/flows/dto/create-flow-request.ts b/packages/shared/src/lib/flows/dto/create-flow-request.ts index 9c5346b2e..990538a0a 100755 --- a/packages/shared/src/lib/flows/dto/create-flow-request.ts +++ b/packages/shared/src/lib/flows/dto/create-flow-request.ts @@ -3,6 +3,7 @@ import { Static, Type } from '@sinclair/typebox' export const CreateFlowRequest = Type.Object({ displayName: Type.String({}), folderId: Type.Optional(Type.String({})), + projectId: Type.String({}), }) export type CreateFlowRequest = Static diff --git a/packages/shared/src/lib/flows/dto/list-flows-request.ts b/packages/shared/src/lib/flows/dto/list-flows-request.ts index c2104e542..d6d3cf072 100755 --- a/packages/shared/src/lib/flows/dto/list-flows-request.ts +++ b/packages/shared/src/lib/flows/dto/list-flows-request.ts @@ -1,10 +1,13 @@ import { Static, Type } from '@sinclair/typebox' import { Cursor } from '../../common/seek-page' +import { FlowStatus } from '../flow' export const ListFlowsRequest = Type.Object({ folderId: Type.Optional(Type.String()), limit: Type.Optional(Type.Number({})), cursor: Type.Optional(Type.String({})), + status: Type.Optional(Type.Enum(FlowStatus)), + projectId: Type.String({}), }) export type ListFlowsRequest = Omit, 'cursor'> & { cursor: Cursor | undefined } @@ -15,3 +18,9 @@ export const GetFlowQueryParamsRequest = Type.Object({ export type GetFlowQueryParamsRequest = Static +export const ListFlowVersionRequest = Type.Object({ + limit: Type.Optional(Type.Number({})), + cursor: Type.Optional(Type.String({})), +}) + +export type ListFlowVersionRequest = Omit, 'cursor'> & { cursor: Cursor | undefined } diff --git a/packages/shared/src/lib/flows/flow-helper.ts b/packages/shared/src/lib/flows/flow-helper.ts index ab868b685..359213aea 100755 --- a/packages/shared/src/lib/flows/flow-helper.ts +++ b/packages/shared/src/lib/flows/flow-helper.ts @@ -627,88 +627,89 @@ export function getImportOperations( type: FlowOperationType.ADD_ACTION, request: { parentStep: step.name, - action: keepBaseAction(step.nextAction), + action: removeAnySubsequentAction(step.nextAction), }, }) } - if (step.type === ActionType.BRANCH) { - if (step.onFailureAction) { - steps.push({ - type: FlowOperationType.ADD_ACTION, - request: { - parentStep: step.name, - stepLocationRelativeToParent: - StepLocationRelativeToParent.INSIDE_FALSE_BRANCH, - action: keepBaseAction(step.onFailureAction), - }, - }) - steps.push(...getImportOperations(step.onFailureAction)) - } - if (step.onSuccessAction) { - steps.push({ - type: FlowOperationType.ADD_ACTION, - request: { - parentStep: step.name, - stepLocationRelativeToParent: - StepLocationRelativeToParent.INSIDE_TRUE_BRANCH, - action: keepBaseAction(step.onSuccessAction), - }, - }) - steps.push(...getImportOperations(step.onSuccessAction)) + switch (step.type) { + case ActionType.BRANCH: { + if (step.onFailureAction) { + steps.push({ + type: FlowOperationType.ADD_ACTION, + request: { + parentStep: step.name, + stepLocationRelativeToParent: + StepLocationRelativeToParent.INSIDE_FALSE_BRANCH, + action: removeAnySubsequentAction(step.onFailureAction), + }, + }) + steps.push(...getImportOperations(step.onFailureAction)) + } + if (step.onSuccessAction) { + steps.push({ + type: FlowOperationType.ADD_ACTION, + request: { + parentStep: step.name, + stepLocationRelativeToParent: + StepLocationRelativeToParent.INSIDE_TRUE_BRANCH, + action: removeAnySubsequentAction(step.onSuccessAction), + }, + }) + steps.push(...getImportOperations(step.onSuccessAction)) + } + break + } + case ActionType.LOOP_ON_ITEMS: { + if (step.firstLoopAction) { + steps.push({ + type: FlowOperationType.ADD_ACTION, + request: { + parentStep: step.name, + stepLocationRelativeToParent: + StepLocationRelativeToParent.INSIDE_LOOP, + action: removeAnySubsequentAction(step.firstLoopAction), + }, + }) + steps.push(...getImportOperations(step.firstLoopAction)) + } + break + + } + case ActionType.CODE: + case ActionType.PIECE: + case TriggerType.PIECE: + case TriggerType.WEBHOOK: + case TriggerType.EMPTY: + { + break } } - if (step.type === ActionType.LOOP_ON_ITEMS && step.firstLoopAction) { - steps.push({ - type: FlowOperationType.ADD_ACTION, - request: { - parentStep: step.name, - stepLocationRelativeToParent: - StepLocationRelativeToParent.INSIDE_LOOP, - action: keepBaseAction(step.firstLoopAction), - }, - }) - steps.push(...getImportOperations(step.firstLoopAction)) - } + + step = step.nextAction } return steps } -// It's better to use switch case, to enforce that all actions are covered -// TODO this can be simplified -function keepBaseAction(action: Action): Action { - const commonProps = { - name: action.name, - displayName: action.displayName, - valid: action.valid, - } - switch (action.type) { - case ActionType.BRANCH: - // PICK type and settings from action - return { - type: ActionType.BRANCH, - settings: action.settings, - ...commonProps, - } - case ActionType.LOOP_ON_ITEMS: - return { - type: ActionType.LOOP_ON_ITEMS, - settings: action.settings, - ...commonProps, - } - case ActionType.CODE: - return { - type: action.type, - settings: action.settings, - ...commonProps, - } + +function removeAnySubsequentAction(action: Action): Action { + const clonedAction: Action = JSON.parse(JSON.stringify(action)) + switch (clonedAction.type) { + case ActionType.BRANCH: { + delete clonedAction.onSuccessAction + delete clonedAction.onFailureAction + break + } + case ActionType.LOOP_ON_ITEMS: { + delete clonedAction.firstLoopAction + break + } case ActionType.PIECE: - return { - type: action.type, - settings: action.settings, - ...commonProps, - } + case ActionType.CODE: + break } + delete clonedAction.nextAction + return clonedAction } function upgradePiece(step: Step, stepName: string): Step { diff --git a/packages/shared/src/lib/flows/flow-operations.ts b/packages/shared/src/lib/flows/flow-operations.ts index 1e16fa7a1..4f78808e1 100755 --- a/packages/shared/src/lib/flows/flow-operations.ts +++ b/packages/shared/src/lib/flows/flow-operations.ts @@ -16,6 +16,7 @@ export enum FlowOperationType { UPDATE_ACTION = 'UPDATE_ACTION', DELETE_ACTION = 'DELETE_ACTION', DUPLICATE_ACTION = 'DUPLICATE_ACTION', + USE_AS_DRAFT = 'USE_AS_DRAFT', } export enum StepLocationRelativeToParent { @@ -27,6 +28,11 @@ export enum StepLocationRelativeToParent { const optionalNextAction = Type.Object({ nextAction: Type.Optional(Action) }) +export const UseAsDraftRequest = Type.Object({ + versionId: Type.String(), +}) +export type UseAsDraftRequest = Static + export const LockFlowRequest = Type.Object({ flowId: Type.String({}), }) @@ -92,6 +98,10 @@ export const FlowOperationRequest = Type.Union([ type: Type.Literal(FlowOperationType.MOVE_ACTION), request: MoveActionRequest, }), + Type.Object({ + type: Type.Literal(FlowOperationType.USE_AS_DRAFT), + request: UseAsDraftRequest, + }), Type.Object({ type: Type.Literal(FlowOperationType.LOCK_FLOW), request: LockFlowRequest, diff --git a/packages/shared/src/lib/flows/flow-version.ts b/packages/shared/src/lib/flows/flow-version.ts index 830eae241..84d5043eb 100755 --- a/packages/shared/src/lib/flows/flow-version.ts +++ b/packages/shared/src/lib/flows/flow-version.ts @@ -1,4 +1,4 @@ -import { BaseModelSchema } from '../common/base-model' +import { BaseModelSchema, Nullable } from '../common/base-model' import { ApId } from '../common/id-generator' import { Trigger } from './triggers/trigger' import { Static, Type } from '@sinclair/typebox' @@ -15,9 +15,19 @@ export const FlowVersion = Type.Object({ flowId: Type.String(), displayName: Type.String(), trigger: Trigger, - updatedBy: Type.String(), + updatedBy: Nullable(Type.String()), valid: Type.Boolean(), state: Type.Enum(FlowVersionState), }) -export type FlowVersion = Static \ No newline at end of file +export type FlowVersion = Static + +export const FlowVersionMetadata = Type.Object({ + ...BaseModelSchema, + flowId: Type.String(), + displayName: Type.String(), + valid: Type.Boolean(), + state: Type.Enum(FlowVersionState), +}) + +export type FlowVersionMetadata = Static diff --git a/packages/shared/src/lib/user/user.ts b/packages/shared/src/lib/user/user.ts index 8949f3472..1cce7631d 100755 --- a/packages/shared/src/lib/user/user.ts +++ b/packages/shared/src/lib/user/user.ts @@ -5,12 +5,10 @@ import { ApId } from '../common/id-generator' export type UserId = ApId export enum UserStatus { - /* user registered but didn't verify their email */ - CREATED = 'CREATED', - /* user registered and verified their email or accepted an invitation */ - VERIFIED = 'VERIFIED', - /* user is disabled */ - SUSPENDED = 'SUSPENDED', + /* user is active */ + ACTIVE = 'ACTIVE', + /* user account deactivated */ + INACTIVE = 'INACTIVE', } export const EmailType = Type.String({ @@ -30,6 +28,7 @@ export const User = Type.Object({ trackEvents: Type.Boolean(), newsLetter: Type.Boolean(), password: Type.String(), + verified: Type.Boolean(), status: Type.Enum(UserStatus), imageUrl: Type.Optional(Type.String()), title: Type.Optional(Type.String()), diff --git a/packages/ui/common/src/index.ts b/packages/ui/common/src/index.ts index f4deb3260..24160f0c4 100644 --- a/packages/ui/common/src/index.ts +++ b/packages/ui/common/src/index.ts @@ -21,6 +21,7 @@ export * from './lib/utils/locales'; export * from './lib/service/platform-project.service'; export * from './lib/service/project.service'; export * from './lib/guards/show-based-on-flag.guard'; +export * from './lib/components/upgrade-note/upgrade-note.component'; import '@angular/localize/init'; export * from './lib/pipe/comma-seperated.pipe'; export * from './lib/utils/consts'; diff --git a/packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html b/packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html index c453267d0..36a12e0ef 100644 --- a/packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html +++ b/packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html @@ -12,8 +12,8 @@
- +
diff --git a/packages/ui/common/src/lib/components/array-form-control/array-form-control.component.ts b/packages/ui/common/src/lib/components/array-form-control/array-form-control.component.ts index 1549d8b87..de7e6183d 100644 --- a/packages/ui/common/src/lib/components/array-form-control/array-form-control.component.ts +++ b/packages/ui/common/src/lib/components/array-form-control/array-form-control.component.ts @@ -24,6 +24,7 @@ export class ArrayFormControlComponent implements ControlValueAccessor { valueChanges$: Observable; formArray: FormArray>; @Input({ required: true }) placeholder = ''; + @Input() addEmptyFieldAfterWriteValue = true; onChange: (val: unknown) => void = () => { //ignore }; @@ -61,7 +62,8 @@ export class ArrayFormControlComponent implements ControlValueAccessor { } if ( this.formArray.length > 0 && - this.formArray.controls[this.formArray.length - 1].value + this.formArray.controls[this.formArray.length - 1].value && + this.addEmptyFieldAfterWriteValue ) { this.formArray.push(new FormControl('', { nonNullable: true })); } diff --git a/packages/ui/common/src/lib/components/dialog-title-template/dialog-title-template.component.ts b/packages/ui/common/src/lib/components/dialog-title-template/dialog-title-template.component.ts index 6ff53d3f9..d6a3dd6a1 100755 --- a/packages/ui/common/src/lib/components/dialog-title-template/dialog-title-template.component.ts +++ b/packages/ui/common/src/lib/components/dialog-title-template/dialog-title-template.component.ts @@ -4,5 +4,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; selector: 'ap-dialog-title-template', templateUrl: './dialog-title-template.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + preserveWhitespaces: false, }) export class DialogTitleTemplateComponent {} diff --git a/packages/ui/common/src/lib/components/page-title/page-title.component.html b/packages/ui/common/src/lib/components/page-title/page-title.component.html index 4d6aa957e..6a432253a 100755 --- a/packages/ui/common/src/lib/components/page-title/page-title.component.html +++ b/packages/ui/common/src/lib/components/page-title/page-title.component.html @@ -1,10 +1,12 @@ -
-
-
- {{ title }} +
+
+
+
+ {{ title }} +
+
BETA
-
BETA
- +
- +
\ No newline at end of file diff --git a/packages/ui/common/src/lib/components/page-title/page-title.component.ts b/packages/ui/common/src/lib/components/page-title/page-title.component.ts index e0126d83a..4e62b7db2 100755 --- a/packages/ui/common/src/lib/components/page-title/page-title.component.ts +++ b/packages/ui/common/src/lib/components/page-title/page-title.component.ts @@ -6,6 +6,6 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class PageTitleComponent { - @Input() title!: string; + @Input({ required: true }) title = ''; @Input() isBeta = false; } diff --git a/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.html b/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.html new file mode 100644 index 000000000..11cde6969 --- /dev/null +++ b/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.html @@ -0,0 +1,14 @@ +
+
+ + Please upgrade your plan to unlock this feature and many more +
+ +
+ + + Upgrade +
+
+
\ No newline at end of file diff --git a/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.ts b/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.ts new file mode 100644 index 000000000..8721ad2ac --- /dev/null +++ b/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.ts @@ -0,0 +1,14 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; + +@Component({ + selector: 'ap-upgrade-note', + templateUrl: './upgrade-note.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UpgradeNoteComponent { + @Input() urlToOpen = 'https://www.activepieces.com/pricing'; + + openUrl() { + window.open(this.urlToOpen, '_blank'); + } +} diff --git a/packages/ui/common/src/lib/components/user-avatar/user-avatar.component.ts b/packages/ui/common/src/lib/components/user-avatar/user-avatar.component.ts index 5a7ad5b3a..8fe62c202 100755 --- a/packages/ui/common/src/lib/components/user-avatar/user-avatar.component.ts +++ b/packages/ui/common/src/lib/components/user-avatar/user-avatar.component.ts @@ -7,7 +7,7 @@ import { FlagService } from '../../service/flag.service'; import { Store } from '@ngrx/store'; import { ProjectSelectors } from '../../store/project/project.selector'; import { LocalesService } from '../../service/locales.service'; -import { LocalesEnum } from '@activepieces/ee-shared'; +import { LocalesEnum } from '@activepieces/shared'; import { localesMap } from '../../utils/locales'; import { PlatformProjectService } from '../../service/platform-project.service'; diff --git a/packages/ui/common/src/lib/directives/card.directive.ts b/packages/ui/common/src/lib/directives/card.directive.ts new file mode 100644 index 000000000..92566a6e7 --- /dev/null +++ b/packages/ui/common/src/lib/directives/card.directive.ts @@ -0,0 +1,18 @@ +import { Directive, ElementRef, OnInit } from '@angular/core'; + +@Directive({ + selector: '[apCard]', +}) +export class CardDirective implements OnInit { + constructor(private eRef: ElementRef) {} + ngOnInit() { + this.eRef.nativeElement.classList.add( + 'ap-rounded', + 'ap-border', + 'ap-border-solid', + 'ap-border-border', + 'ap-p-4', + 'ap-shadow' + ); + } +} diff --git a/packages/ui/common/src/lib/directives/index.ts b/packages/ui/common/src/lib/directives/index.ts index 7160ca235..41153bdcf 100644 --- a/packages/ui/common/src/lib/directives/index.ts +++ b/packages/ui/common/src/lib/directives/index.ts @@ -6,3 +6,4 @@ export * from './drag-drop.directive'; export * from './element-ref.directive'; export * from './element-ref.directive'; export * from './image-fallback.directive'; +export * from './card.directive'; diff --git a/packages/ui/common/src/lib/service/embedding.service.ts b/packages/ui/common/src/lib/service/embedding.service.ts index e181ffa65..a79200c57 100644 --- a/packages/ui/common/src/lib/service/embedding.service.ts +++ b/packages/ui/common/src/lib/service/embedding.service.ts @@ -32,7 +32,7 @@ export class EmbeddingService { getIsInEmbedding$() { return this.getState$().pipe(map((res) => res.isEmbedded)); } - getShowFolderNameAndBackButton$() { + getShowNavigationInBuilder$() { return this.getState$().pipe(map((res) => !res.disableNavigationInBuilder)); } diff --git a/packages/ui/common/src/lib/service/flow.service.ts b/packages/ui/common/src/lib/service/flow.service.ts index c7e8b3676..cc727ef66 100755 --- a/packages/ui/common/src/lib/service/flow.service.ts +++ b/packages/ui/common/src/lib/service/flow.service.ts @@ -14,6 +14,7 @@ import { FlowOperationRequest, FlowOperationType, FlowRun, + FlowVersion, FlowVersionId, ListFlowsRequest, MakeKeyNonNullableAndRequired, @@ -22,16 +23,21 @@ import { TestFlowRunRequestBody, UpdateFlowStatusRequest, } from '@activepieces/shared'; +import { AuthenticationService } from './authentication.service'; export const CURRENT_FLOW_IS_NEW_KEY_IN_LOCAL_STORAGE = 'newFlow'; @Injectable({ providedIn: 'root', }) export class FlowService { - constructor(private http: HttpClient) {} + constructor( + private http: HttpClient, + private authenticationService: AuthenticationService + ) {} create(request: CreateFlowRequest): Observable { return this.http.post(environment.apiUrl + '/flows', { displayName: request.displayName, folderId: request.folderId, + projectId: request.projectId, }); } @@ -74,6 +80,7 @@ export class FlowService { switchMap((flow) => { return this.create({ displayName: flow.version.displayName, + projectId: this.authenticationService.getProjectId(), }).pipe( switchMap((clonedFlow) => { return this.update(clonedFlow.id, { @@ -103,6 +110,7 @@ export class FlowService { limit: request.limit ?? 10, cursor: request.cursor || '', }; + queryParams['projectId'] = request.projectId; if (request.folderId) { queryParams['folderId'] = request.folderId; } @@ -124,6 +132,12 @@ export class FlowService { ); } + listVersions(flowId: FlowId): Observable> { + return this.http.get>( + environment.apiUrl + '/flows/' + flowId + '/versions' + ); + } + execute(request: TestFlowRunRequestBody): Observable { return this.http .post(environment.apiUrl + '/flow-runs/test', request) diff --git a/packages/ui/common/src/lib/service/locales.service.ts b/packages/ui/common/src/lib/service/locales.service.ts index c838389e5..88280da1b 100755 --- a/packages/ui/common/src/lib/service/locales.service.ts +++ b/packages/ui/common/src/lib/service/locales.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { Location } from '@angular/common'; -import { isNil } from '@activepieces/shared'; -import { LocalesEnum } from '@activepieces/ee-shared'; +import { isNil, LocalesEnum } from '@activepieces/shared'; import { localesMap } from '../utils/locales'; @Injectable({ providedIn: 'root' }) diff --git a/packages/ui/common/src/lib/service/platform.service.ts b/packages/ui/common/src/lib/service/platform.service.ts index fb9b764b0..9b3c821b4 100644 --- a/packages/ui/common/src/lib/service/platform.service.ts +++ b/packages/ui/common/src/lib/service/platform.service.ts @@ -2,6 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Platform, UpdatePlatformRequestBody } from '@activepieces/ee-shared'; import { environment } from '../environments/environment'; +import { SeekPage, UserResponse, UserStatus } from '@activepieces/shared'; @Injectable({ providedIn: 'root', @@ -20,4 +21,13 @@ export class PlatformService { `${environment.apiUrl}/platforms/${platformId}` ); } + listUsers() { + return this.http.get>(`${environment.apiUrl}/users`); + } + + updateUser(userId: string, { status }: { status: UserStatus }) { + return this.http.post(`${environment.apiUrl}/users/${userId}`, { + status, + }); + } } diff --git a/packages/ui/common/src/lib/ui-common.module.ts b/packages/ui/common/src/lib/ui-common.module.ts index 3ae11c4b1..bff327f1e 100644 --- a/packages/ui/common/src/lib/ui-common.module.ts +++ b/packages/ui/common/src/lib/ui-common.module.ts @@ -66,6 +66,8 @@ import { ImgFallbackDirective } from './directives/image-fallback.directive'; import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar'; import { ScrollingModule } from '@angular/cdk/scrolling'; import { ArrayFormControlComponent } from './components/array-form-control/array-form-control.component'; +import { UpgradeNoteComponent } from '././components/upgrade-note/upgrade-note.component'; +import { CardDirective } from './directives/card.directive'; const exportedImports = [ CommonModule, MatTooltipModule, @@ -128,6 +130,8 @@ const exportedDeclarations = [ PoweredByActivepiecesComponent, ImgFallbackDirective, ArrayFormControlComponent, + UpgradeNoteComponent, + CardDirective, ]; export const materialTooltipDefaults: MatTooltipDefaultOptions = { showDelay: 0, diff --git a/packages/ui/common/src/lib/utils/locales.ts b/packages/ui/common/src/lib/utils/locales.ts index 3c46e3097..5943a854f 100644 --- a/packages/ui/common/src/lib/utils/locales.ts +++ b/packages/ui/common/src/lib/utils/locales.ts @@ -1,10 +1,11 @@ -import { LocalesEnum } from '@activepieces/ee-shared'; +import { LocalesEnum } from '@activepieces/shared'; export const localesMap = { [LocalesEnum.ENGLISH]: 'English', [LocalesEnum.SPANISH]: 'Espaรฑol', [LocalesEnum.FRENCH]: 'Franรงais', [LocalesEnum.GERMAN]: 'Deutsch', + [LocalesEnum.HUNGARIAN]: 'Magyar', [LocalesEnum.ITALIAN]: 'Italiano', [LocalesEnum.DUTCH]: 'Nederlands', [LocalesEnum.JAPANESE]: 'ๆ—ฅๆœฌ่ชž', diff --git a/packages/ui/core/nginx.standard.conf b/packages/ui/core/nginx.standard.conf index bd323e20a..cf3ba5073 100644 --- a/packages/ui/core/nginx.standard.conf +++ b/packages/ui/core/nginx.standard.conf @@ -15,6 +15,7 @@ http { ~*^vi vi; ~*^zh zh; ~*^pt pt; + ~*^hu hu; } server { @@ -39,13 +40,13 @@ http { # Everything under the Angular application is always redirected to Angular in the # correct language - location ~ ^/(fr|de|it|pt|nl|ja|es|zh|pt|vi|id) { + location ~ ^/(fr|de|it|pt|nl|ja|es|zh|pt|vi|id|hu) { root /usr/share/nginx/html; try_files $uri $uri/ /$1/index.html?$args; } - location ~ ^/(fr|de|it|pt|nl|ja|es|zh|pt|vi|id)$ { - rewrite ^/(fr|de|it|pt|nl|ja|es|zh|pt|vi|id)$ /$1/ permanent; + location ~ ^/(fr|de|it|pt|nl|ja|es|zh|pt|vi|id|hu)$ { + rewrite ^/(fr|de|it|pt|nl|ja|es|zh|pt|vi|id|hu)$ /$1/ permanent; } # USe the default language for the root of the application diff --git a/packages/ui/core/project.json b/packages/ui/core/project.json index 23c054007..ca524a8f8 100644 --- a/packages/ui/core/project.json +++ b/packages/ui/core/project.json @@ -169,6 +169,7 @@ "locales": { "es": "packages/ui/core/src/locale/messages.es.xlf", "fr": "packages/ui/core/src/locale/messages.fr.xlf", + "hu": "packages/ui/core/src/locale/messages.hu.xlf", "id": "packages/ui/core/src/locale/messages.id.xlf", "zh": "packages/ui/core/src/locale/messages.zh.xlf", "pt": "packages/ui/core/src/locale/messages.pt.xlf", diff --git a/packages/ui/core/src/app/app.component.ts b/packages/ui/core/src/app/app.component.ts index 163919919..c9b093092 100644 --- a/packages/ui/core/src/app/app.component.ts +++ b/packages/ui/core/src/app/app.component.ts @@ -32,6 +32,7 @@ import { compareVersions } from 'compare-versions'; import { ApEdition, ApFlagId, + LocalesEnum, FlowOperationType, User, } from '@activepieces/shared'; @@ -48,7 +49,7 @@ import { FlowsActions, } from '@activepieces/ui/feature-builder-store'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { LocalesEnum, Platform } from '@activepieces/ee-shared'; +import { Platform } from '@activepieces/ee-shared'; interface UpgradeNotificationMetaDataInLocalStorage { latestVersion: string; diff --git a/packages/ui/core/src/app/modules/import-flow-uri-encoded/import-flow-uri-encoded.resolver.ts b/packages/ui/core/src/app/modules/import-flow-uri-encoded/import-flow-uri-encoded.resolver.ts index 518b1414e..ee40b40f8 100644 --- a/packages/ui/core/src/app/modules/import-flow-uri-encoded/import-flow-uri-encoded.resolver.ts +++ b/packages/ui/core/src/app/modules/import-flow-uri-encoded/import-flow-uri-encoded.resolver.ts @@ -1,7 +1,11 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { EMPTY, Observable, catchError, switchMap, tap } from 'rxjs'; -import { FlowService, TelemetryService } from '@activepieces/ui/common'; +import { + FlowService, + TelemetryService, + AuthenticationService, +} from '@activepieces/ui/common'; import { FlowOperationType, FlowTemplate, @@ -23,6 +27,7 @@ export class ImportFlowUriEncodedResolver { const combinationJson: FlowTemplate = JSON.parse(decodedFlow); return this.flowService .create({ + projectId: this.authenticationService.getProjectId(), displayName: combinationJson.name, }) .pipe( @@ -60,6 +65,7 @@ export class ImportFlowUriEncodedResolver { constructor( private flowService: FlowService, private router: Router, + private authenticationService: AuthenticationService, private telemetryService: TelemetryService ) {} } diff --git a/packages/ui/core/src/app/modules/import-flow/import-flow.component.ts b/packages/ui/core/src/app/modules/import-flow/import-flow.component.ts index e7f56cea4..a61aee51f 100644 --- a/packages/ui/core/src/app/modules/import-flow/import-flow.component.ts +++ b/packages/ui/core/src/app/modules/import-flow/import-flow.component.ts @@ -9,6 +9,7 @@ import { FlowService, RedirectService, TelemetryService, + AuthenticationService, TemplatesService, } from '@activepieces/ui/common'; import { HttpErrorResponse } from '@angular/common/http'; @@ -45,6 +46,7 @@ export class ImportFlowComponent implements OnInit { private router: Router, private metaService: Meta, private telemetryService: TelemetryService, + private authenticationService: AuthenticationService, private flagService: FlagService, private redirectService: RedirectService ) { @@ -88,6 +90,7 @@ export class ImportFlowComponent implements OnInit { switchMap(() => { return this.flowService .create({ + projectId: this.authenticationService.getProjectId(), displayName: templateJson.template.displayName, }) .pipe( diff --git a/packages/ui/core/src/assets/img/custom/auth/domain.svg b/packages/ui/core/src/assets/img/custom/auth/domain.svg new file mode 100644 index 000000000..dcb758e83 --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/auth/domain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/auth/email.svg b/packages/ui/core/src/assets/img/custom/auth/email.svg new file mode 100644 index 000000000..c49be1edf --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/auth/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/connection.png b/packages/ui/core/src/assets/img/custom/connection.png new file mode 100644 index 000000000..f78ad52fb Binary files /dev/null and b/packages/ui/core/src/assets/img/custom/connection.png differ diff --git a/packages/ui/core/src/assets/img/custom/dashboard/dashboard.svg b/packages/ui/core/src/assets/img/custom/dashboard/dashboard.svg deleted file mode 100644 index 1d0912955..000000000 --- a/packages/ui/core/src/assets/img/custom/dashboard/dashboard.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/ui/core/src/assets/img/custom/dashboard/home.svg b/packages/ui/core/src/assets/img/custom/dashboard/home.svg new file mode 100644 index 000000000..750c26f86 --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/dashboard/home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/dashboard/users.svg b/packages/ui/core/src/assets/img/custom/dashboard/users.svg new file mode 100644 index 000000000..57ca6a92e --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/dashboard/users.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/disconnect.svg b/packages/ui/core/src/assets/img/custom/disconnect.svg new file mode 100644 index 000000000..8e92680e1 --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/disconnect.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/draft.svg b/packages/ui/core/src/assets/img/custom/draft.svg new file mode 100644 index 000000000..419ccb43d --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/draft.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/git.svg b/packages/ui/core/src/assets/img/custom/git.svg new file mode 100644 index 000000000..3cb0ead6f --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/git.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/lock.svg b/packages/ui/core/src/assets/img/custom/lock.svg new file mode 100644 index 000000000..20b9e3984 --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/published.svg b/packages/ui/core/src/assets/img/custom/published.svg new file mode 100644 index 000000000..5f52bddea --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/published.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/reactivate.svg b/packages/ui/core/src/assets/img/custom/reactivate.svg new file mode 100644 index 000000000..a858a3d6a --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/reactivate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/suspend.svg b/packages/ui/core/src/assets/img/custom/suspend.svg new file mode 100644 index 000000000..5bf1080d6 --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/suspend.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/upgrade.svg b/packages/ui/core/src/assets/img/custom/upgrade.svg new file mode 100644 index 000000000..df9c43213 --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/upgrade.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/upload.svg b/packages/ui/core/src/assets/img/custom/upload.svg new file mode 100644 index 000000000..8e6dccaf0 --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/img/custom/versions.svg b/packages/ui/core/src/assets/img/custom/versions.svg new file mode 100644 index 000000000..3f7c900b2 --- /dev/null +++ b/packages/ui/core/src/assets/img/custom/versions.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/core/src/assets/scss/ng-material-override.scss b/packages/ui/core/src/assets/scss/ng-material-override.scss index 10956a84a..0c6fddcb4 100644 --- a/packages/ui/core/src/assets/scss/ng-material-override.scss +++ b/packages/ui/core/src/assets/scss/ng-material-override.scss @@ -730,5 +730,19 @@ cdk-virtual-scroll-viewport } + +app-platform-settings { + .mat-mdc-tab-body-wrapper , .mat-mdc-tab-group-dynamic-height .mat-mdc-tab-body-content + { + overflow: visible; + } +} +.mat-mdc-outlined-button:disabled +{ + svg{ + fill: var(--mdc-outlined-button-disabled-label-text-color, rgba(0, 0, 0, 0.38)) !important + } +} + diff --git a/packages/ui/core/src/locale/messages.bg.xlf b/packages/ui/core/src/locale/messages.bg.xlf new file mode 100644 index 000000000..a8b92fb22 --- /dev/null +++ b/packages/ui/core/src/locale/messages.bg.xlf @@ -0,0 +1,3841 @@ + + + + + + + You have reached the limits of your current plan. โญ to unlock more features. + + + + + + packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.html + 17,20 + + + ะ’ะธะต ะดะพัั‚ะธะณะฝะฐั…ั‚ะต ะดะพ ะปะธะผะธั‚ะฐ ะฝะฐ ัะตะณะฐั‰ะฝะธัั‚ ะฒะธ ะฟะปะฐะฝ. โญ ะทะฐ ะดะฐ ะพั‚ะบะปัŽั‡ะธั‚ะต ะพั‰ะต ั„ัƒะฝะบั†ะธะธ. + + + + + + + Please contact your platform admin + + packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.ts + 26 + + ะœะพะปั ัะฒัŠั€ะถะตั‚ะต ัะต ั ะฐะดะผะธะฝะธัั‚ั€ะฐั‚ะพั€ะฐ ะฝะฐ ะฒะฐัˆะฐั‚ะฐ ะฟะปะฐั‚ั„ะพั€ะผะฐ + + + Upgrade + + packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.ts + 27 + + ะะฐะดะณั€ะฐะถะดะฐะฝะต + + + Share template + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 8 + + ะกะฟะพะดะตะปัะฝะต ะฝะฐ ัˆะฐะฑะปะพะฝ + + + Description + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 18 + + ะžะฟะธัะฐะฝะธะต + + + Blog Url + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 24 + + Url ะฝะฐ ะฑะปะพะณะฐ + + + Filters + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 27 + + ะคะธะปั‚ั€ะธ + + + Cancel + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 42,44 + + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 33,35 + + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 26,28 + + + packages/ui/core/src/app/modules/import-flow/import-flow.component.html + 33,34 + + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 28,30 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 26,28 + + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 22,24 + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 56,58 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 72,74 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 72,74 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 98,100 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 48,50 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 26,28 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 24,26 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 24,26 + + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 58,60 + + ะžั‚ะบะฐะท + + + Confirm + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 46,48 + + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 29,31 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 29,31 + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + + ะŸะพั‚ะฒัŠั€ะถะดะฐะฒะฐะผ + + + OR + + packages/ee/components/src/lib/third-party-auth/third-party-auth.component.html + 29,31 + + ะ˜ะ›ะ˜ + + + Sign up with + + packages/ee/components/src/lib/third-party-auth/third-party-auth.component.ts + 21 + + ะ ะตะณะธัั‚ั€ะธั€ะฐะนั‚ะต ัะต ั‡ั€ะตะท + + + Sign in with + + packages/ee/components/src/lib/third-party-auth/third-party-auth.component.ts + 22 + + ะ’ะฟะธัˆะตั‚ะต ัะต ั‡ั€ะตะท + + + Team Invitation Accepted + + packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html + 3,5 + + ะŸะพะบะฐะฝะฐั‚ะฐ ะทะฐ ะพั‚ะฑะพั€ ะต ะฟั€ะธะตั‚ะฐ + + + Thank you for accepting the invitation. We are redirecting you right now... + + packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html + 6,8 + + ะ‘ะปะฐะณะพะดะฐั€ะธะผ ะฒะธ, ั‡ะต ะฟั€ะธะตั…ั‚ะต ะฟะพะบะฐะฝะฐั‚ะฐ. ะะธะต ะฒะธ ะฟั€ะตะฝะฐัะพั‡ะฒะฐะผะต ัะตะณะฐ... + + + Invalid invitation token. Please try again. + + packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html + 11,13 + + ะะตะฒะฐะปะธะดะตะฝ ั‚ะพะบัŠะฝ ะทะฐ ะฟะพะบะฐะฝะฐ. ะœะพะปั ะพะฟะธั‚ะฐะนั‚ะต ะพั‚ะฝะพะฒะพ. + + + Invite Project Member + + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 1,2 + + ะŸะพะบะฐะฝะฐ ะฝะฐ ั‡ะปะตะฝ ะฝะฐ ะฟั€ะพะตะบั‚ะฐ + + + + Type the email address of the user you want to invite. + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 5,7 + + ะ’ัŠะฒะตะดะตั‚ะต email ะฐะดั€ะตัะฐ ะฝะฐ ะฟะพั‚ั€ะตะฑะธั‚ะตะปั ะบะพะนั‚ะพ ะถะตะปะฐะตั‚ะต ะดะฐ ะฟะพะบะฐะฝะธั‚ะต. + + + The invited email already has an account. Please contact support for assistance. + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 13 + + ะŸะพะบะฐะฝะตะฝะธัั‚ email ะฒะตั‡ะต ะธะผะฐ ะฐะบะฐัƒะฝั‚. ะœะพะปั ัะฒัŠั€ะถะตั‚ะต ัะต ั ะฟะพะดะดั€ัŠะถะบะฐั‚ะฐ ะทะฐ ะฐัะธัั‚ะตะฝั†ะธั. + + + Email is Required + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 16 + + Email ะต ะทะฐะดัŠะปะถะธั‚ะตะปะตะฝ + + + Role + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 21 + + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 19 + + ะ ะพะปั + + + Invite + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 37,39 + + ะŸะพะบะฐะฝะฐ + + + Internal error occurred please contact support + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 65 + + ะ’ัŠะทะฝะธะบะฝะฐ ะฒัŠั‚ั€ะตัˆะฝะฐ ะณั€ะตัˆะบะฐ, ะผะพะปั ัะฒัŠั€ะถะตั‚ะต ัะต ั ะฟะพะดะดั€ัŠะถะบะฐั‚ะฐ + + + ADMIN + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 96 + + ะะ”ะœะ˜ะะ˜ะกะขะ ะะขะžะ  + + + EDITOR + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 98 + + ะ ะ•ะ”ะะšะขะžะ  + + + VIEWER + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 100 + + ะŸะ ะ•ะ“ะ›ะ•ะ–ะ”ะะฉ + + + UNKNOWN + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 102 + + ะะ•ะŸะžะ—ะะะข + + + Project Members + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 5 + + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts + 44 + + ะงะปะตะฝะพะฒะต ะฝะฐ ะฟั€ะพะตะบั‚ะฐ + + + Email + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 7 + + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 6 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 26 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 27 + + Email + + + Status + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 14 + + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 18 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 32 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 34 + + ะกั‚ะฐั‚ัƒั + + + Invited At + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 24 + + ะŸะพะบะฐะฝะตะฝ ะฝะฐ + + + + Invite + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 31 + + + ะŸะพะบะฐะฝะฐ + + + Delete Invitation + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 36 + + ะ˜ะทั‚ั€ะธะฒะฐะฝะต ะฝะฐ ะฟะพะบะฐะฝะฐั‚ะฐ + + + Active + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts + 141 + + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 88 + + ะะบั‚ะธะฒะฝะฐ + + + Pending + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts + 143 + + ะงะฐะบะฐั‰ะฐ + + + Name + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 16 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 12 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 15 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 16 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 15 + + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 13 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 14 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 9 + + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 17 + + ะ˜ะผะต + + + Created + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 25 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 27 + + ะกัŠะทะดะฐะดะตะฝะฐ + + + Create + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 27,29 + + ะกัŠะทะดะฐะฒะฐะฝะต + + + Edit + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 83 + + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 6 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 6 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 6 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 6 + + ะ ะตะดะฐะบั‚ะธั€ะฐะฝะต + + + Save + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 59,61 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 76,78 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 75,77 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 101,103 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 51,53 + + ะ—ะฐะฟะฐะทะฒะฐะฝะต + + + Projects + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 41 + + ะŸั€ะพะตะบั‚ะธ + + + Appearance + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 47 + + ะ˜ะทะณะปะตะด + + + Pieces + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 53 + + ะŸะฐั€ั‡ะตั‚ะฐ + + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + ะจะฐะฑะปะพะฝะธ + + + Settings + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 71 + + ะะฐัั‚ั€ะพะนะบะธ + + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + ะŸะพั‚ั€ะตะฑะธั‚ะตะปะธ + + + Version + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 22 + + ะ’ะตั€ัะธั + + + Updated + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 + + ะžะฑะฝะพะฒะตะฝะพ + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + ะ”ะพะฑะฐะฒัะฝะต ะฝะฐ ะตะปะตะผะตะฝั‚ + + + + Delete + + + + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 1,2 + + + ะ˜ะทั‚ั€ะธะฒะฐะฝะต + + + + + + + Type DELETE and press confirm to continue. + + + + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 7,9 + + + ะ˜ะทะฟะธัˆะตั‚ะต DELETE ะธ ะฝะฐั‚ะธัะฝะตั‚ะต ะฟะพั‚ะฒัŠั€ะถะดะฐะฒะฐะฝะต ะทะฐ ะดะฐ ะฟั€ะพะดัŠะปะถะธั‚ะต. + + + + + + Please write the confirmation message correctly + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 14,16 + + Please write the confirmation message correctly + + + Close + + packages/ui/common/src/lib/components/json-view/json-view-dialog/json-view-dialog.component.html + 13,15 + + Close + + + BETA + + packages/ui/common/src/lib/components/page-title/page-title.component.html + 6 + + BETA + + + Page Size: + + packages/ui/common/src/lib/components/pagination/ap-paginator.component.html + 2 + + Page Size: + + + Succeeded + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 71 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 10 + + Succeeded + + + Internal Error + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 75 + + Internal Error + + + Timed Out + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 77 + + Timed Out + + + Failed + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 80 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 9 + + Failed + + + Paused + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 83 + + Paused + + + Running + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 86 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 8 + + Running + + + Error + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 90 + + Error + + + My Pieces + + packages/ui/common/src/lib/components/user-avatar/user-avatar.component.html + 62 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 53 + + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 3 + + My Pieces + + + Logout + + packages/ui/common/src/lib/components/user-avatar/user-avatar.component.html + 74 + + Logout + + + An unexpected error occured, please contact support + + packages/ui/common/src/lib/utils/consts.ts + 1 + + An unexpected error occured, please contact support + + + Webhook Trigger + + packages/ui/common/src/lib/utils/utils.ts + 6 + + Webhook Trigger + + + Empty Trigger + + packages/ui/common/src/lib/utils/utils.ts + 10 + + Empty Trigger + + + Trigger + + packages/ui/common/src/lib/utils/utils.ts + 13 + + Trigger + + + Code + + packages/ui/common/src/lib/utils/utils.ts + 22 + + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 195 + + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/code-step-input-form/code-step-input-form.component.html + 12 + + Code + + + Loop on Items + + packages/ui/common/src/lib/utils/utils.ts + 25 + + Loop on Items + + + Branch + + packages/ui/common/src/lib/utils/utils.ts + 31 + + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 193 + + Branch + + + Import Flow + + packages/ui/core/src/app/app-routing.module.ts + 25 + + Import Flow + + + Upgrade + + packages/ui/core/src/app/app.component.html + 7,8 + + Upgrade + + + Ignore + + packages/ui/core/src/app/app.component.html + 9,10 + + Ignore + + + Steps in this flow + + packages/ui/core/src/app/modules/import-flow/import-flow.component.html + 17 + + Steps in this flow + + + Template Description + + packages/ui/core/src/app/modules/import-flow/import-flow.component.html + 24 + + Template Description + + + Use Template + + packages/ui/core/src/app/modules/import-flow/import-flow.component.html + 36,37 + + Use Template + + + Page not found ๐Ÿ˜ข + + packages/ui/core/src/app/modules/not-found/not-found.component.html + 5 + + Page not found ๐Ÿ˜ข + + + Looks like you are lost. + + packages/ui/core/src/app/modules/not-found/not-found.component.html + 6 + + Looks like you are lost. + + + Return Home + + packages/ui/core/src/app/modules/not-found/not-found.component.html + 7 + + Return Home + + + redirect-url works! + + packages/ui/core/src/app/modules/redirect-url/redirect-url.component.html + 1 + + redirect-url works! + + + + Didn't receive an email? Resend + + + + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.html + 14,19 + + + Didn't receive an email? Resend + + + + + + Go back to sign in + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.html + 19 + + Go back to sign in + + + We sent you a link to complete your registration, check your email. + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts + 29 + + We sent you a link to complete your registration, check your email. + + + We sent you a link to reset your password, check your email. + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts + 30 + + We sent you a link to reset your password, check your email. + + + Verification resent + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts + 51 + + Verification resent + + + Password reset link resent + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts + 75 + + Password reset link resent + + + Reset Password + + packages/ui/feature-authentication/src/lib/pages/auth-actions/reset-password/reset-password.component.ts + 23 + + Reset Password + + + Your password reset request has expired, please request a new one + + packages/ui/feature-authentication/src/lib/pages/auth-actions/reset-password/reset-password.component.ts + 60 + + Your password reset request has expired, please request a new one + + + + + + + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html + 9,13 + + + + + + + + + Your email has been verified. You will be redirected to sign in + + + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html + 13,17 + + + Your email has been verified. You will be redirected to sign in + + + + + + This invitation has expired, redirecting to sign in... + + + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html + 17,19 + + + This invitation has expired, redirecting to sign in... + + + + + Sign in + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html + 20 + + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 52 + + Sign in + + + Verifying Email + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 21 + + Verifying Email + + + Verified Email + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 22 + + Verified Email + + + Verification Failed + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 23 + + Verification Failed + + + Verifying email + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 38 + + Verifying email + + + Email Verified + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 61 + + Email Verified + + + Forgot Password + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 3 + + Forgot Password + + + If the user exists we'll send you an email with a link to reset your password. + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 4,6 + + If the user exists we'll send you an email with a link to reset your password. + + + + Email is required Email is invalid + + + + + + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 12,20 + + + Email is required Email is invalid + + + + + + + + Send email + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 25,27 + + Send email + + + Back to sign in + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 31 + + Back to sign in + + + Welcome Back + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 1 + + Welcome Back + + + Email is invalid + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 8,10 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 33,35 + + Email is invalid + + + Email is required + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 11 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 39,40 + + Email is required + + + Password + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 15 + + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 16 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 47 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 49 + + Password + + + Password is required + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 17 + + Password is required + + + Invalid email or password + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 19 + + Invalid email or password + + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + You are not invited to any project, please contact your administrator. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Your user is inactive, please contact your administrator to activate it. + + + Sign in + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 36,38 + + Sign in + + + Don't have an account? + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 43 + + Don't have an account? + + + Sign up now + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 43 + + Sign up now + + + Forgot password? + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 47 + + Forgot password? + + + Let's Get Started! + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 3,5 + + Let's Get Started! + + + First Name + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 10 + + First Name + + + First name is required + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 12,14 + + First name is required + + + Last Name + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 17 + + Last Name + + + Last name is required + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 19,21 + + Last name is required + + + Email is used + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 36,38 + + Email is used + + + Password is required + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 51,53 + + Password is required + + + Password is invalid + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 54 + + Password is invalid + + + Lowercase + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 71 + + Lowercase + + + Uppercase + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 81 + + Uppercase + + + Special Character + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 92 + + Special Character + + + Number + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 101 + + Number + + + 8-64 Characters + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 114 + + 8-64 Characters + + + Receive updates and newsletters from activepieces + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 120,123 + + Receive updates and newsletters from activepieces + + + Sign up + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 126,128 + + Sign up + + + By creating an account, you agree to our + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 134 + + By creating an account, you agree to our + + + terms of service + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 136,137 + + terms of service + + + and + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 138 + + and + + + privacy policy + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 139 + + privacy policy + + + Already have an account? + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 143 + + Already have an account? + + + Signing up is disabled. + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 150 + + Signing up is disabled. + + + If you are the owner, please refer to the configuration section in the documentation to learn how to enable it. + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 151,154 + + If you are the owner, please refer to the configuration section in the documentation to learn how to enable it. + + + Sign up + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 60 + + Sign up + + + Verify email + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 68 + + Verify email + + + Reset password + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 78 + + Reset password + + + Forgot password + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 88 + + Forgot password + + + True + + packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts + 71 + + True + + + False + + packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts + 71 + + False + + + Delete Step + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/delete-flow-item-action/delete-flow-item-action.component.html + 1 + + Delete Step + + + Duplicate Step + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/duplicate-step-action/duplicate-step-action.component.html + 1 + + Duplicate Step + + + Change Trigger + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/replace-trigger-action/replace-trigger-action.component.html + 1 + + Change Trigger + + + Can't move here + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/add-button-core.component.ts + 21 + + Can't move here + + + Incomplete settings + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.html + 35 + + Incomplete settings + + + Loop + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 197 + + Loop + + + Choose a trigger + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 207 + + Choose a trigger + + + Webhook trigger + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 209 + + Webhook trigger + + + End + + + packages/ui/feature-builder-canvas/src/lib/components/widgets/end-of-flow-widget/end-of-flow-widget.component.html + 2,4 + + End + + + + + Complete step + + + + + packages/ui/feature-builder-canvas/src/lib/components/widgets/incomplete-steps-widget/incomplete-steps-widget.component.html + 5,7 + + + Complete step + + + + + + + + + + packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.html + 11,13 + + + + + + + Test flow + + packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts + 52 + + Test flow + + + Saving... + + packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts + 53 + + Saving... + + + View Only + + + packages/ui/feature-builder-canvas/src/lib/components/widgets/view-only-mode-widget/view-only-mode-widget.component.html + 2,4 + + View Only + + + + + Value + + + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 5 + + + Value + + + + + First value is required + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 9,11 + + First value is required + + + Condition + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 32 + + Condition + + + Condition is required + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 38,40 + + Condition is required + + + Second Value + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 47 + + Second Value + + + Second value is required + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 51,53 + + Second value is required + + + Case sensitive + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 64,66 + + Case sensitive + + + And + + packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html + 11,13 + + And + + + + And + + packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html + 21,23 + + + And + + + + Or + + packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html + 25,27 + + + Or + + + Add NPM Package + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 1 + + Add NPM Package + + + + Please type the name of an NPM package. + + + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 3,6 + + + Please type the name of an NPM package. + + + + + Package name is required + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 11,13 + + Package name is required + + + Package not found + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 15,17 + + Package not found + + + The latest version will be fetched and added to package.json + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 21,23 + + The latest version will be fetched and added to package.json + + + Add + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 31,33 + + Add + + + Code Editor + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 3 + + Code Editor + + + Add npm package + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 6,8 + + Add npm package + + + Test code + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 10,11 + + Test code + + + Minimize Editor + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 13 + + Minimize Editor + + + Output + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 74,76 + + Output + + + Console + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 81,83 + + Console + + + Copied to clipboard + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts + 198 + + Copied to clipboard + + + Test Step First + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html + 42,44 + + Test Step First + + + This step needs to be tested in order to view its data. + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html + 45,47 + + This step needs to be tested in order to view its data. + + + Go to step + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html + 50,52 + + Go to step + + + + Describe the path to the property using JSON format + + + + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 4,6 + + + Describe the path to the property using JSON format + + + + + + + Path is invalid + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 12,14 + + Path is invalid + + + + Path must begin with + + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 16,18 + + + Path must begin with + + + + + Select + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/generic-mention-item/generic-mention-item.component.html + 4,5 + + Select + + + + result: + + + + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html + 37,40 + + + result: + + + + + + + Insert + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html + 43,44 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/step-mentions-tree/step-mentions-tree.component.html + 19,20 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/step-mentions-tree/step-mentions-tree.component.html + 44,45 + + Insert + + + This trigger needs to have data loaded from your account, to use as sample data + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html + 56,58 + + This trigger needs to have data loaded from your account, to use as sample data + + + Go to trigger + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html + 61,62 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html + 33,34 + + Go to trigger + + + Send sample data first + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html + 25,27 + + Send sample data first + + + This trigger needs to have data sent to it, to use as sample data + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html + 28,30 + + This trigger needs to have data sent to it, to use as sample data + + + Support + + packages/ui/feature-builder-header/src/lib/feedback/support.component.html + 6 + + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 37 + + Support + + + Dashboard + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 14 + + Dashboard + + + Rename + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 43 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 57 + + Rename + + + Duplicate + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 50 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 77 + + Duplicate + + + Import + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 56 + + Import + + + Export + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 63 + + Export + + + Delete + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 72 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 84 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 64 + + Delete + + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + + + This will permanently delete the flow, all its data and any background runs. + You can't undo this action. + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 141,142 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 119,120 + + This will permanently delete the flow, all its data and any background runs. + You can't undo this action. + + + Import Flow + + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 1,3 + + Import Flow + + + + + Important: Importing a flow will overwrite your current flow. + + + + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 9 + + + Important: Importing a flow will overwrite your current flow. + + + + + + File + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 12 + + File + + + Import + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 26,28 + + Import + + + Edit Flow + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.html + 16,18 + + Edit Flow + + + Add 1 more step to publish + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 86 + + Add 1 more step to publish + + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + Published + + + Your flow has invalid steps + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 90 + + Your flow has invalid steps + + + Publish Flow + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 92 + + Publish Flow + + + Saving + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 98 + + Saving + + + Publishing + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 100 + + Publishing + + + Publish + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 102 + + Publish + + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + + + Run Details + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/run-details.component.html + 2 + + Run Details + + + No details available for this run. + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/run-details.component.html + 49 + + No details available for this run. + + + Step succeeded + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 10 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 30 + + Step succeeded + + + Step paused + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 14 + + Step paused + + + Duration: + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 22 + + Duration: + + + Output + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 27 + + Output + + + Done + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/steps-results-list/step-result.component.html + 45,47 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 27,29 + + Done + + + All Iterations + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/steps-results-list/step-result.component.html + 72,74 + + All Iterations + + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Draft + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> + In that case, you will also have to add an <b>HTTP step</b> with return response to the end of your flow.<br> <br> + **If the flow takes more than 30 seconds, it will give a 408 Request Timeout response.**<br> + + + + + + packages/ui/feature-builder-right-sidebar/src/lib/edit-step-sidebar/edit-step-form-container/edit-step-form-container.component.html + 17,21 + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> + In that case, you will also have to add an <b>HTTP step</b> with return response to the end of your flow.<br> <br> + **If the flow takes more than 30 seconds, it will give a 408 Request Timeout response.**<br> + + + + + + + Edit step name + + packages/ui/feature-builder-right-sidebar/src/lib/edit-step-sidebar/step-name-editor/step-name-editor.component.html + 6 + + Edit step name + + + Or + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/branch-step-input-form/branch-step-input-form.component.html + 11,13 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 19,21 + + Or + + + Inputs + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/code-step-input-form/code-step-input-form.component.html + 4 + + Inputs + + + Items + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/loop-step-input-form/loop-step-input-form.component.html + 4 + + Items + + + Items are required. + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/loop-step-input-form/loop-step-input-form.component.html + 7,9 + + Items are required. + + + Action + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html + 4 + + Action + + + No options available + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html + 23,25 + + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 40,42 + + No options available + + + Action is required + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html + 27,29 + + Action is required + + + Trigger + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 4 + + Trigger + + + Select a Trigger + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 5 + + Select a Trigger + + + Instantly triggered once change occurs + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 21 + + Instantly triggered once change occurs + + + Checks for changes periodically, based on your plan + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 29 + + Checks for changes periodically, based on your plan + + + Trigger is required + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 44,46 + + Trigger is required + + + Request Piece + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-list/step-type-list.component.html + 5,7 + + Request Piece + + + Search + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.html + 7 + + + packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html + 10 + + Search + + + Select Trigger + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 73 + + Select Trigger + + + Select Step + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 75 + + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 80 + + Select Step + + + All + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 166 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 7 + + All + + + Oops! We didn't find any results. + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 168 + + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 174 + + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 182 + + Oops! We didn't find any results. + + + Core + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 172 + + Core + + + App Events + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 179 + + App Events + + + App Actions + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 180 + + App Actions + + + Uncategorized + + packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts + 112 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html + 21 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.datasource.ts + 102 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 26 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 11 + + + packages/ui/feature-dashboard/src/lib/store/folders/folders.effects.ts + 22 + + Uncategorized + + + + Run succeeded () + + + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 7 + + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 11 + + + Run succeeded () + + + + + + Run failed () + + + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 15 + + + Run failed () + + + + + Flow is running + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 19 + + Flow is running + + + Flow is paused ... + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 23 + + Flow is paused ... + + + + Run exceeded seconds, try to optimize your steps. + + + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 27,28 + + + Run exceeded seconds, try to optimize your steps. + + + + + Run failed for an unknown reason, contact support. + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 32 + + Run failed for an unknown reason, contact support. + + + Exit Run + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 39 + + Exit Run + + + Generate Sample Data + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 2,4 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 2,4 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 2,4 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 2,4 + + Generate Sample Data + + + Test step + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 40,41 + + Test step + + + Tested Successfully + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 60 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 31 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 24 + + Tested Successfully + + + Testing Failed + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 63 + + Testing Failed + + + Retest + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 72 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 40 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 30 + + Retest + + + Test this trigger to generate sample data that can be used in the next steps + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 10 + + Test this trigger to generate sample data that can be used in the next steps + + + Test Trigger + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 16,17 + + Test Trigger + + + Use Mock Data + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 22,23 + + Use Mock Data + + + Data + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 44,46 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 40,42 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 34,36 + + Data + + + + (Result ) + + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 49 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 45 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 39 + + + (Result ) + + + + + Testing trigger + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 64 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 54 + + Testing trigger + + + Cancel + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 67 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 57 + + Cancel + + + Action Required + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 73 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 63 + + Action Required + + + Load sample data from your account to be used in the next steps + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 13 + + Load sample data from your account to be used in the next steps + + + Load data + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 18 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 36 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 69 + + Load data + + + This sample data can be used in the next steps + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 49,51 + + This sample data can be used in the next steps + + + No results found + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 60 + + No results found + + + Please add some data then press "Load Data" + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 72 + + Please add some data then press "Load Data" + + + Testing data failed. Please ensure that the settings are correct or try again. + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 73,74 + + Testing data failed. Please ensure that the settings are correct or try again. + + + Send a request to generate sample data that can be used in the next steps + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 10 + + Send a request to generate sample data that can be used in the next steps + + + Send Data + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 13,15 + + Send Data + + + Please send data to the webhook endpoint to test the step. + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 65,68 + + Please send data to the webhook endpoint to test the step. + + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + New Connection + + + Name is required + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 20,22 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 15,17 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 18,20 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 19,21 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 19,21 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 14,16 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 14,16 + + Name is required + + + Name can only contain letters, numbers and underscores + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 24,26 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 19,21 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 22,24 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 23,25 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 23,25 + + Name can only contain letters, numbers and underscores + + + Name is already used + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 27,29 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 22,24 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 25,27 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 26,28 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 26,28 + + Name is already used + + + is required + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 38 + + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 47 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 35 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 43 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 56 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 36 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 48 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 60 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 73 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 37 + + is required + + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Reconnect + + + You have to connect an account to continue + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 58,60 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 86,88 + + You have to connect an account to continue + + + I would like to use my own app credentials + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 64,67 + + I would like to use my own app credentials + + + The ID of this connection definition. You will need to select this key whenever you want to reuse this connection. + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.ts + 75 + + The ID of this connection definition. You will need to select this key whenever you want to reuse this connection. + + + Redirect URL is required + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 37,38 + + Redirect URL is required + + + Client ID + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 42 + + Client ID + + + Client ID is required + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 44,45 + + Client ID is required + + + Client Secret + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 48 + + Client Secret + + + Client Secret is required + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 51,52 + + Client Secret is required + + + I would like to use predefined app credentials + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 92,93 + + I would like to use predefined app credentials + + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 2,4 + + New Connection + + + Connect + + packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html + 2 + + Connect + + + Disconnect + + packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html + 4 + + Disconnect + + + Please make sure this connections's credentials are correct. + + + packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html + 5,7 + + Please make sure this connections's credentials are correct. + + + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Home + + + Ask the Community + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 29 + + Ask the Community + + + Documentation + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 39 + + Documentation + + + Docs + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 47 + + Docs + + + Flows + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 92 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 69 + + Flows + + + Runs + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 101 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 29 + + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 5 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 3 + + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 9 + + Runs + + + Connections + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 107 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 61 + + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts + 35 + + Connections + + + Team + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 113 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 45 + + Team + + + What's new + + packages/ui/feature-dashboard/src/lib/dashboard-container.component.html + 15,17 + + What's new + + + Plans + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 37 + + Plans + + + Platform + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 81 + + Platform + + + App + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 7 + + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 11 + + App + + + Delete Connection + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 43 + + Delete Connection + + + No connections created yet + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 59,61 + + No connections created yet + + + Create Your First Flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 5 + + Create Your First Flow + + + Demo flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 15 + + Demo flow + + + Learn from our Gelato maker demo flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 16,18 + + Learn from our Gelato maker demo flow + + + Build flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 37 + + Build flow + + + Start building your first flow from scratch + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 38,40 + + Start building your first flow from scratch + + + Untitled + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.ts + 41 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts + 41 + + Untitled + + + All flows + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html + 6 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 15 + + All flows + + + + New Flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html + 26 + + + New Flow + + + My Flows + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 4 + + My Flows + + + Steps + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 21 + + Steps + + + Folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 54 + + Folder + + + Move to... + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 70 + + Move to... + + + You don't have any flows yet + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 101 + + You don't have any flows yet + + + This folder doesn't have any flows yet + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 104 + + This folder doesn't have any flows yet + + + Real time flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 193 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 200 + + Real time flow + + + + Runs + + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 197,199 + + + Runs + + + + + Please contact support as your published flow has a problem + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 207 + + Please contact support as your published flow has a problem + + + Please publish the flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 234 + + Please publish the flow + + + Flow is on + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 238 + + Flow is on + + + Flow is off + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 239 + + Flow is off + + + Folders + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 2 + + Folders + + + New Folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 7 + + New Folder + + + Ascending + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.ts + 140 + + Ascending + + + Descending + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.ts + 143 + + Descending + + + New Folder + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 1,2 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 1,2 + + New Folder + + + + Name your new folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 4,6 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 4,6 + + Name your new folder + + + Select Folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 9 + + Select Folder + + + No folders to move to + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 17 + + No folders to move to + + + Must select a folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 19 + + Must select a folder + + + Move + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 29,31 + + Move + + + Name is already used + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 17 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 17 + + Name is already used + + + + Rename Folder () + + + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 1,2 + + + Rename Folder () + + + + + + Change the name of your folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 4,6 + + Change the name of your folder + + + Filter by status + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 5 + + Filter by status + + + Send email notifications about failed runs + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 14,16 + + Send email notifications about failed runs + + + Flow + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 20 + + Flow + + + Started + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 25 + + Started + + + Finished + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 30 + + Finished + + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + + + No runs instantiated yet + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 72,74 + + No runs instantiated yet + + + This is where you can install pieces made by yourself or contributors. + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 4,6 + + This is where you can install pieces made by yourself or contributors. + + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Add Piece + + + No pieces installed + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 49,51 + + No pieces installed + + + Install Piece + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 1 + + Install Piece + + + Piece name + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 7 + + Piece name + + + NPM package name + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 8 + + NPM package name + + + This is required + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 11,13 + + This is required + + + Install + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 61,63 + + Install + + + + Use this to install a <a href="https://www.activepieces.com/docs/developers/building-pieces/create-action" target="_blank" rel="noopener">custom piece</a> that you (or someone else) created. + Once the piece is installed, you can use it in the flow builder. + <br><br>**Warning:** + Make sure you trust the author as the piece will have access to your flow data and it might not be compatible with the current version of Activepieces. + + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.ts + 35,40 + + + Use this to install a <a href="https://www.activepieces.com/docs/developers/building-pieces/create-action" target="_blank" rel="noopener">custom piece</a> that you (or someone else) created. + Once the piece is installed, you can use it in the flow builder. + <br><br>**Warning:** + Make sure you trust the author as the piece will have access to your flow data and it might not be compatible with the current version of Activepieces. + + + + Learn about this flow + + packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html + 3,5 + + Learn about this flow + + + You used a template for this flow and you can read our guide on how to set it up. + + packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html + 6,8 + + You used a template for this flow and you can read our guide on how to set it up. + + + Go to guide + + packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html + 12 + + Go to guide + + + Ignore + + packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html + 18,20 + + Ignore + + + Use Template + + packages/ui/feature-templates/src/lib/template-card/template-card.component.html + 8,10 + + Use Template + + + Apps + + + packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html + 1,3 + + Apps + + + + Select + + packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html + 6 + + Select + + + Browse Templates + + packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html + 3 + + Browse Templates + + + Search templates + + packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html + 34 + + Search templates + + + Start from scratch + + packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html + 47 + + Start from scratch + + + Filters + + packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html + 2,4 + + Filters + + + + diff --git a/packages/ui/core/src/locale/messages.de.xlf b/packages/ui/core/src/locale/messages.de.xlf index c3063d2d5..d982023fc 100644 --- a/packages/ui/core/src/locale/messages.de.xlf +++ b/packages/ui/core/src/locale/messages.de.xlf @@ -60,27 +60,11 @@ Link zum Blog - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - Bild url - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Vorgeschlagene Beschreibung - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 Filter @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + Bestรคtigen @@ -458,22 +450,6 @@ Erstellt - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbots - Create @@ -486,7 +462,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -534,7 +510,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 Projekte @@ -542,7 +518,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 Aussehen @@ -550,18 +526,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Bausteine + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Templates + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 Einstellungen + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Users + Version @@ -570,13 +562,29 @@ Version - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - Baustein hinzufรผgen + Aktualisiert + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Element hinzufรผgen @@ -718,7 +726,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -806,18 +814,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 Ablauf importieren - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - Chatbot - Upgrade @@ -1176,11 +1176,31 @@ Ungรผltige E-Mail oder Kennwort + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + You are not invited to any project, please contact your administrator. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Your user is inactive, please contact your administrator to activate it. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 Anmelden @@ -1188,7 +1208,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Sie besitzen noch kein Konto? @@ -1196,7 +1216,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Sign up now @@ -1204,7 +1224,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 Forgot password? @@ -1272,14 +1292,6 @@ Das Kennwort ist ungรผltig - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Contact your admin to send you an invite - Lowercase @@ -1546,7 +1558,7 @@ Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 Test flow @@ -1554,7 +1566,7 @@ Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 Saving... @@ -1568,18 +1580,6 @@ Nur Ansicht - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Element hinzufรผgen - Value @@ -1918,50 +1918,6 @@ Diesem Auslรถser mรผssen zunรคchst Daten gesendet werden, um ihn fรผr Beispieldaten zu verwenden - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Version wechseln - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Entwurf - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - Verรถffentlicht - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - Verรถffentlicht - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Entwurf - Support @@ -1974,23 +1930,19 @@ Support - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Startseite + Dashboard Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -2002,7 +1954,7 @@ Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2014,7 +1966,7 @@ Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 Importieren @@ -2022,7 +1974,7 @@ Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 Exportieren @@ -2030,7 +1982,7 @@ Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2042,16 +1994,24 @@ Lรถschen + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 Dies lรถscht den Ablauf, all seine Daten und alle Hintergrundausfรผhrungen unwiderruflich. Sie kรถnnen diese Aktion nicht rรผckgรคngig machen. @@ -2110,15 +2070,27 @@ Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 Fรผgen Sie einen weiteren Schritt hinzu, bevor Sie verรถffentlichen kรถnnen + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + Verรถffentlicht + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 Ihr Ablauf hat ungรผltige Schritte @@ -2126,7 +2098,7 @@ Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 Ablauf verรถffentlichen @@ -2134,7 +2106,7 @@ Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 Speichere @@ -2142,7 +2114,7 @@ Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 Verรถffentliche @@ -2150,10 +2122,72 @@ Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 Verรถffentlichen + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + Run Details @@ -2230,6 +2264,54 @@ Alle Durchlรคufe + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Entwurf + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2460,7 +2542,7 @@ Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2808,6 +2890,26 @@ Bitte senden Sie Daten zum Webhook Endpunkt, um den Schritt zu testen. + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + Neue Verbindung + Name is required @@ -2840,34 +2942,6 @@ Der Name ist erforderlich - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Neuverbinden - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - Neue Verbindung - Name can only contain letters, numbers and underscores @@ -2960,6 +3034,14 @@ ist erforderlich + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Neuverbinden + You have to connect an account to continue @@ -3070,6 +3152,14 @@ Bitte stellen Sie sicher, dass die Zugangsdaten dieser Verbindung korrekt sind. + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Startseite + Ask the Community @@ -3098,11 +3188,11 @@ Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 Ablรคufe @@ -3110,11 +3200,11 @@ Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3134,11 +3224,11 @@ Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3150,11 +3240,11 @@ Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 Team @@ -3170,27 +3260,15 @@ Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 Plรคne - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Chatbot Einstellungen - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 Plattform @@ -3206,14 +3284,6 @@ Anwendung - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - Aktualisiert - Delete Connection @@ -3226,7 +3296,7 @@ No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 Es wurde noch keine Verbindung erstellt @@ -3278,7 +3348,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 Unbenannt @@ -3354,11 +3424,11 @@ Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 Echtzeitablauf @@ -3369,7 +3439,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 Ausfรผhrungen @@ -3380,7 +3450,7 @@ Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 Bitte kontaktieren Sie den Support, da Ihr verรถffentlichter Ablauf ein Problem hat @@ -3574,11 +3644,23 @@ Abgeschlossen + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 Noch keine Ausfรผhrungen instanziiert @@ -3590,6 +3672,14 @@ Hier kรถnnen Sie Bausteine installieren, die von Ihnen oder Mitwirkenden erstellt wurden. + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Baustein hinzufรผgen + No pieces installed @@ -3656,46 +3746,6 @@ Vergewissern Sie sich, dass Sie dem Author vertrauen kรถnnen, da der Baustein Zugriff auf die Daten haben wird und mรถglicherweise nicht kompatibel mit der aktuellen Version von Activepieces ist. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Activepieces Team - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - Weniger anzeigen - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - Mehr anzeigen - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Vorlage verwenden - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Verifizierte Icons erstellt von lakonicon - Flaticon - Learn about this flow @@ -3754,59 +3804,19 @@ Auswรคhlen - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - Vorgeschlagen - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Mehr laden - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - Alle Ideen - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 Vorlagen durchstรถbern - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - Ein neuer Weg coole Ideen von unseren Partnern zu erkunden - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - Zeige vorgeschlagene Ideen - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 Suche Vorlagen @@ -3814,19 +3824,17 @@ Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 Beginne von Grund auf - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Filter - + Filters diff --git a/packages/ui/core/src/locale/messages.es.xlf b/packages/ui/core/src/locale/messages.es.xlf index 21a3b1171..773d8d648 100644 --- a/packages/ui/core/src/locale/messages.es.xlf +++ b/packages/ui/core/src/locale/messages.es.xlf @@ -60,27 +60,11 @@ Url del Blog - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - Url de la imagen - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Descripciรณn destacada - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 Filtros @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + Confirmar @@ -458,22 +450,6 @@ Creado - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbots - Create @@ -486,7 +462,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -534,7 +510,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 Proyectos @@ -542,7 +518,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 Apariencia @@ -550,18 +526,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Piezas + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Templates + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 Opciones + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Users + Version @@ -570,13 +562,29 @@ Versiรณn - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - Agregar pieza + Actualizada + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Agregar Elemento @@ -718,7 +726,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -806,18 +814,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 Flujo Importante - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - Chatbot - Upgrade @@ -1176,11 +1176,31 @@ Email o contraseรฑa invรกlidos + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + You are not invited to any project, please contact your administrator. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Your user is inactive, please contact your administrator to activate it. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 Entrar @@ -1188,7 +1208,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 ยฟNo tienes cuenta? @@ -1196,7 +1216,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Regรญstrate ahora @@ -1204,7 +1224,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 ยฟOlvidaste la contraseรฑa? @@ -1272,14 +1292,6 @@ La contraseรฑa es invรกlida - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Contacta a tu administrador para que te envรญe una invitaciรณn - Lowercase @@ -1424,21 +1436,21 @@ Contraseรฑa olvidada - + True packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts 71 - Verdadero + Verdadero - + False packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts 71 - Falso + Falso Delete Step @@ -1464,13 +1476,13 @@ Cambiar Disparador - + Can't move here packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/add-button-core.component.ts 21 - No se puede mover aquรญ + No se puede mover aquรญ Incomplete settings @@ -1504,14 +1516,14 @@ Disparador Webhook - + End packages/ui/feature-builder-canvas/src/lib/components/widgets/end-of-flow-widget/end-of-flow-widget.component.html 2,4 - Fin + Fin @@ -1530,7 +1542,7 @@ - + @@ -1538,25 +1550,25 @@ packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.html 11,13 - + - + Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 - Probar Flujo + Probar Flujo - + Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 - Guardando... + Guardando... View Only @@ -1568,18 +1580,6 @@ Solo Ver - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Agregar Elemento - Value @@ -1744,45 +1744,45 @@ Probar cรณdigo - + Minimize Editor packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 13 - Minimize Editor + Minimizar Editor - + Output packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 74,76 - Output + Salida - + Console packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 81,83 - Console + Consola - + Copied to clipboard packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts 198 - Copied to clipboard + Copiado al portapapeles - + Test Step First packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 42,44 - Test Step First + Prueba el Paso Primero This step needs to be tested in order to view its data. @@ -1918,50 +1918,6 @@ Este disparador necesita tener datos enviados a รฉl, para usar datos de ejemplo - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Cambiar versiรณn - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Borrador - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - Publicado - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - Publicado - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Borrador - Support @@ -1974,23 +1930,19 @@ Soporte - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Inicio + Dashboard Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -2002,7 +1954,7 @@ Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2014,7 +1966,7 @@ Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 Importar @@ -2022,7 +1974,7 @@ Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 Exportar @@ -2030,7 +1982,7 @@ Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2042,16 +1994,24 @@ Eliminar + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 Esto eliminarรก permanentemente tu flujo, todos sus datos y ejecuciones. Esta acciรณn no se puede deshacer. @@ -2110,15 +2070,27 @@ Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 Agrega un paso mรกs para publicar + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + Publicado + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 Tu flujo tiene pasos invรกlidos @@ -2126,7 +2098,7 @@ Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 Publicar Flujo @@ -2134,7 +2106,7 @@ Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 Guardando @@ -2142,7 +2114,7 @@ Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 Publicando @@ -2150,10 +2122,72 @@ Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 Publicar + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + Run Details @@ -2230,6 +2264,54 @@ Todas las iteraciones + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Borrador + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2460,7 +2542,7 @@ Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2808,6 +2890,26 @@ Por favor, envรญa datos al endpoint del webhook para probar este paso. + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + Nueva conexiรณn + Name is required @@ -2840,34 +2942,6 @@ El nombre es requerido - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Reconectar - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - Nueva conexiรณn - Name can only contain letters, numbers and underscores @@ -2960,6 +3034,14 @@ es requerido + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Reconectar + You have to connect an account to continue @@ -3070,6 +3152,14 @@ Asegรบrate de que los credenciales de esta conexiรณn sean correctas. + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Inicio + Ask the Community @@ -3098,11 +3188,11 @@ Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 Flujos @@ -3110,11 +3200,11 @@ Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3134,11 +3224,11 @@ Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3150,11 +3240,11 @@ Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 Equipo @@ -3170,27 +3260,15 @@ Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 Planes - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Opciones del chatbot - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 Plataforma @@ -3206,14 +3284,6 @@ App - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - Actualizada - Delete Connection @@ -3226,7 +3296,7 @@ No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 Sin conexiones creadas @@ -3278,7 +3348,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 Sin tรญtulo @@ -3354,11 +3424,11 @@ Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 Flujo en tiempo real @@ -3369,7 +3439,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 Ejecuciones @@ -3380,7 +3450,7 @@ Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 Pรณngase en contacto con soporte tรฉcnico, ya que su flujo publicado tiene un problema @@ -3574,11 +3644,23 @@ Finalizado + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 No se han iniciado ejecuciones aรบn @@ -3590,6 +3672,14 @@ Aquรญ es donde puedes instalar Piezas hechas por ti mismo o por colaboradores. + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Agregar pieza + No pieces installed @@ -3656,46 +3746,6 @@ Asegรบrate de que confias en el autor, ya que la Pieza podrรก acceder a los datos de tu flujo y podrรญa no ser compatible con la versiรณn actual de Activepieces. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Equipo de Activepieces - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - Ver menos - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - Ver mรกs - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Usar plantilla - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - รconos verificados creados por lakonicon - Flaticon - Learn about this flow @@ -3754,59 +3804,19 @@ Seleccionar - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - Destacado - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Cargar mรกs - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - Todas las ideas - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 Buscar plantillas - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - Una nueva forma de explorar ideas nuevas de nuestros socios - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - Ver ideas destacadas - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 Buscar plantillas @@ -3814,19 +3824,17 @@ Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 Empezar desde cero - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Filtros - + Filters diff --git a/packages/ui/core/src/locale/messages.fr.xlf b/packages/ui/core/src/locale/messages.fr.xlf index 390db52a5..61f6ab4df 100644 --- a/packages/ui/core/src/locale/messages.fr.xlf +++ b/packages/ui/core/src/locale/messages.fr.xlf @@ -60,27 +60,11 @@ Url du blog - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - Url de l'image - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Description de la fonctionnalitรฉ - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 Filtres @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + Valider @@ -458,22 +450,6 @@ Crรฉรฉ - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbots - Create @@ -486,7 +462,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -534,7 +510,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 Projets @@ -542,7 +518,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 Apparence @@ -550,18 +526,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Morceaux + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Modรจles + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 Paramรจtres + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Utilisateurs + Version @@ -570,13 +562,29 @@ Version - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 + + Mis ร  jour + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 - Ajouter un morceau + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Ajouter un รฉlรฉment @@ -718,7 +726,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -806,18 +814,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 Importer Flow - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - Chatbot - Upgrade @@ -1176,11 +1176,31 @@ Email ou mot de passe invalide + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + Vous n'รชtes invitรฉ ร  aucun projet, veuillez contacter votre administrateur. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Votre utilisateur est inactif, veuillez contacter votre administrateur pour l'activer. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 Se connecter @@ -1188,7 +1208,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Vous n'avez pas de compte ? @@ -1196,7 +1216,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Inscrivez-vous @@ -1204,7 +1224,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 Mot de passe oubliรฉ? @@ -1272,14 +1292,6 @@ Le mot de passe n'est pas valide - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Contactez votre administrateur pour vous envoyer une invitation - Lowercase @@ -1546,7 +1558,7 @@ Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 Tester le process @@ -1554,7 +1566,7 @@ Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 Enregistrement... @@ -1568,18 +1580,6 @@ Lecture seul - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Ajouter un รฉlรฉment - Value @@ -1918,50 +1918,6 @@ Ce dรฉclencheur a besoin que les donnรฉes lui soient envoyรฉes, pour l'utiliser comme donnรฉes d'exemple - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Changer de version - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Brouillon - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - Publiรฉ - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - Publiรฉ - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Brouillon - Support @@ -1974,23 +1930,19 @@ Support - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Accueil + Dashboard Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -2002,7 +1954,7 @@ Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2014,7 +1966,7 @@ Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 Importer @@ -2022,7 +1974,7 @@ Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 Exporter @@ -2030,7 +1982,7 @@ Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2042,16 +1994,24 @@ Supprimer + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 Ceci supprimera dรฉfinitivement le process, toutes ses donnรฉes et toutes les exรฉcutions en arriรจre-plan. Cette action est irrรฉversible. @@ -2110,15 +2070,27 @@ Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 Ajouter 1 รฉtape supplรฉmentaire pour publier + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + Publiรฉ + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 Votre process comporte des รฉtapes non valides @@ -2126,7 +2098,7 @@ Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 Publier le process @@ -2134,7 +2106,7 @@ Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 Sauvegarde @@ -2142,7 +2114,7 @@ Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 Publication @@ -2150,10 +2122,72 @@ Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 Publier + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + Run Details @@ -2230,6 +2264,54 @@ Toutes les itรฉrations + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Brouillon + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2460,7 +2542,7 @@ Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2808,6 +2890,26 @@ Veuillez envoyer des donnรฉes ร  l'endpoint du webhook pour tester l'รฉtape. + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + Nouvelle connexion + Name is required @@ -2840,34 +2942,6 @@ Le nom est requis - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Reconnecter - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - Nouvelle connexion - Name can only contain letters, numbers and underscores @@ -2960,6 +3034,14 @@ est requis + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Reconnecter + You have to connect an account to continue @@ -3070,6 +3152,14 @@ Veuillez vous assurer que les informations d'identification de ces connexions sont correctes. + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Accueil + Ask the Community @@ -3098,11 +3188,11 @@ Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 Process @@ -3110,11 +3200,11 @@ Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3134,11 +3224,11 @@ Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3150,11 +3240,11 @@ Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 ร‰quipe @@ -3170,27 +3260,15 @@ Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 Abonnements - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Paramรจtres du Chatbot - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 Plateforme @@ -3206,14 +3284,6 @@ App - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - Mis ร  jour - Delete Connection @@ -3226,7 +3296,7 @@ No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 Aucune connexion n'a encore รฉtรฉ crรฉรฉe @@ -3278,7 +3348,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 Sans titre @@ -3354,11 +3424,11 @@ Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 Process en temps rรฉel @@ -3369,7 +3439,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 Exรฉcutions @@ -3380,7 +3450,7 @@ Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 Veuillez contacter l'assistance, car votre process publiรฉ prรฉsente un problรจme. @@ -3573,11 +3643,23 @@ Fini + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 Pas encore d'exรฉcutions instanciรฉes @@ -3589,6 +3671,14 @@ C'est ici que vous pouvez installer des piรจces faites par vous-mรชme ou par des contributeurs. + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Ajouter un connecteur + No pieces installed @@ -3651,46 +3741,6 @@ Utilisez-le pour installer un <a href="https://www.activepieces.com/docs/developers/building-pieces/create-action" target="_blank" rel="noopener">connecteur personnalisรฉe</a> que vous (ou quelqu'un autre) a crรฉรฉ. Une fois la piรจce installรฉe, vous pouvez l'utiliser dans le gรฉnรฉrateur de process. <br><br>**Attention :** Assurez-vous de faire confiance ร  l'auteur, car l'article aura accรจs ร  vos donnรฉes de process et il se peut qu'il ne le soit pas compatible avec la version actuelle d'Activepieces. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - L'รฉquipe d'Activepieces - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - Voir moins - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - Voir plus - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Utiliser un modรจle - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Icรดnes vรฉrifiรฉes crรฉรฉes par lakonicon - Flaticon - Learn about this flow @@ -3749,59 +3799,19 @@ Utilisez-le pour installer un <a href="https://www.activepieces.com/docs/deve Sรฉlectionner - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - Populaire - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Charger plus - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - Toutes les idรฉes - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 Parcourir les modรจles - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - Une nouvelle faรงon d'explorer les nouvelles idรฉes de nos partenaires - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - Voir les idรฉes en vedette - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 Rechercher un modรจle @@ -3809,19 +3819,17 @@ Utilisez-le pour installer un <a href="https://www.activepieces.com/docs/deve Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 Commencer ร  partir de zรฉro - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Filtres - + Filtres diff --git a/packages/ui/core/src/locale/messages.hi.xlf b/packages/ui/core/src/locale/messages.hi.xlf new file mode 100644 index 000000000..72bb4c278 --- /dev/null +++ b/packages/ui/core/src/locale/messages.hi.xlf @@ -0,0 +1,3841 @@ + + + + + + + You have reached the limits of your current plan. โญ to unlock more features. + + + + + + packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.html + 17,20 + + + You have reached the limits of your current plan. โญ to unlock more features. + + + + + + + Please contact your platform admin + + packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.ts + 26 + + Please contact your platform admin + + + Upgrade + + packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.ts + 27 + + Upgrade + + + Share template + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 8 + + Share template + + + Description + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 18 + + Description + + + Blog Url + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 24 + + Blog Url + + + Filters + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 27 + + Filters + + + Cancel + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 42,44 + + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 33,35 + + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 26,28 + + + packages/ui/core/src/app/modules/import-flow/import-flow.component.html + 33,34 + + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 28,30 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 26,28 + + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 22,24 + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 56,58 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 72,74 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 72,74 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 98,100 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 48,50 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 26,28 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 24,26 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 24,26 + + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 58,60 + + Cancel + + + Confirm + + packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html + 46,48 + + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 29,31 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 29,31 + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + + Confirm + + + OR + + packages/ee/components/src/lib/third-party-auth/third-party-auth.component.html + 29,31 + + OR + + + Sign up with + + packages/ee/components/src/lib/third-party-auth/third-party-auth.component.ts + 21 + + Sign up with + + + Sign in with + + packages/ee/components/src/lib/third-party-auth/third-party-auth.component.ts + 22 + + Sign in with + + + Team Invitation Accepted + + packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html + 3,5 + + Team Invitation Accepted + + + Thank you for accepting the invitation. We are redirecting you right now... + + packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html + 6,8 + + Thank you for accepting the invitation. We are redirecting you right now... + + + Invalid invitation token. Please try again. + + packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html + 11,13 + + Invalid invitation token. Please try again. + + + Invite Project Member + + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 1,2 + + Invite Project Member + + + + Type the email address of the user you want to invite. + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 5,7 + + Type the email address of the user you want to invite. + + + The invited email already has an account. Please contact support for assistance. + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 13 + + The invited email already has an account. Please contact support for assistance. + + + Email is Required + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 16 + + Email is Required + + + Role + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 21 + + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 19 + + Role + + + Invite + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html + 37,39 + + Invite + + + Internal error occurred please contact support + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 65 + + Internal error occurred please contact support + + + ADMIN + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 96 + + ADMIN + + + EDITOR + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 98 + + EDITOR + + + VIEWER + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 100 + + VIEWER + + + UNKNOWN + + packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts + 102 + + UNKNOWN + + + Project Members + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 5 + + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts + 44 + + Project Members + + + Email + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 7 + + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 6 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 26 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 27 + + Email + + + Status + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 14 + + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 18 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 32 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 34 + + Status + + + Invited At + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 24 + + Invited At + + + + Invite + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 31 + + + Invite + + + Delete Invitation + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html + 36 + + Delete Invitation + + + Active + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts + 141 + + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 88 + + Active + + + Pending + + packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts + 143 + + Pending + + + Name + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 16 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 12 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 15 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 16 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 15 + + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 13 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 14 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 9 + + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 17 + + Name + + + Created + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 25 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 27 + + Created + + + Create + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 27,29 + + Create + + + Edit + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 83 + + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 6 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 6 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 6 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 6 + + Edit + + + Save + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 59,61 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 76,78 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 75,77 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 101,103 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 51,53 + + Save + + + Projects + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 41 + + Projects + + + Appearance + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 47 + + Appearance + + + Pieces + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 53 + + Pieces + + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Templates + + + Settings + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 71 + + Settings + + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Users + + + Version + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 22 + + Version + + + Updated + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 + + Updated + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Add Item + + + + Delete + + + + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 1,2 + + + Delete + + + + + + + Type DELETE and press confirm to continue. + + + + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 7,9 + + + Type DELETE and press confirm to continue. + + + + + + Please write the confirmation message correctly + + packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html + 14,16 + + Please write the confirmation message correctly + + + Close + + packages/ui/common/src/lib/components/json-view/json-view-dialog/json-view-dialog.component.html + 13,15 + + Close + + + BETA + + packages/ui/common/src/lib/components/page-title/page-title.component.html + 6 + + BETA + + + Page Size: + + packages/ui/common/src/lib/components/pagination/ap-paginator.component.html + 2 + + Page Size: + + + Succeeded + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 71 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 10 + + Succeeded + + + Internal Error + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 75 + + Internal Error + + + Timed Out + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 77 + + Timed Out + + + Failed + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 80 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 9 + + Failed + + + Paused + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 83 + + Paused + + + Running + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 86 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 8 + + Running + + + Error + + packages/ui/common/src/lib/components/status-icon/state-icon.component.ts + 90 + + Error + + + My Pieces + + packages/ui/common/src/lib/components/user-avatar/user-avatar.component.html + 62 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 53 + + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 3 + + My Pieces + + + Logout + + packages/ui/common/src/lib/components/user-avatar/user-avatar.component.html + 74 + + Logout + + + An unexpected error occured, please contact support + + packages/ui/common/src/lib/utils/consts.ts + 1 + + An unexpected error occured, please contact support + + + Webhook Trigger + + packages/ui/common/src/lib/utils/utils.ts + 6 + + Webhook Trigger + + + Empty Trigger + + packages/ui/common/src/lib/utils/utils.ts + 10 + + Empty Trigger + + + Trigger + + packages/ui/common/src/lib/utils/utils.ts + 13 + + Trigger + + + Code + + packages/ui/common/src/lib/utils/utils.ts + 22 + + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 195 + + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/code-step-input-form/code-step-input-form.component.html + 12 + + Code + + + Loop on Items + + packages/ui/common/src/lib/utils/utils.ts + 25 + + Loop on Items + + + Branch + + packages/ui/common/src/lib/utils/utils.ts + 31 + + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 193 + + Branch + + + Import Flow + + packages/ui/core/src/app/app-routing.module.ts + 25 + + Import Flow + + + Upgrade + + packages/ui/core/src/app/app.component.html + 7,8 + + Upgrade + + + Ignore + + packages/ui/core/src/app/app.component.html + 9,10 + + Ignore + + + Steps in this flow + + packages/ui/core/src/app/modules/import-flow/import-flow.component.html + 17 + + Steps in this flow + + + Template Description + + packages/ui/core/src/app/modules/import-flow/import-flow.component.html + 24 + + Template Description + + + Use Template + + packages/ui/core/src/app/modules/import-flow/import-flow.component.html + 36,37 + + Use Template + + + Page not found ๐Ÿ˜ข + + packages/ui/core/src/app/modules/not-found/not-found.component.html + 5 + + Page not found ๐Ÿ˜ข + + + Looks like you are lost. + + packages/ui/core/src/app/modules/not-found/not-found.component.html + 6 + + Looks like you are lost. + + + Return Home + + packages/ui/core/src/app/modules/not-found/not-found.component.html + 7 + + Return Home + + + redirect-url works! + + packages/ui/core/src/app/modules/redirect-url/redirect-url.component.html + 1 + + redirect-url works! + + + + Didn't receive an email? Resend + + + + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.html + 14,19 + + + Didn't receive an email? Resend + + + + + + Go back to sign in + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.html + 19 + + Go back to sign in + + + We sent you a link to complete your registration, check your email. + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts + 29 + + We sent you a link to complete your registration, check your email. + + + We sent you a link to reset your password, check your email. + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts + 30 + + We sent you a link to reset your password, check your email. + + + Verification resent + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts + 51 + + Verification resent + + + Password reset link resent + + packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts + 75 + + Password reset link resent + + + Reset Password + + packages/ui/feature-authentication/src/lib/pages/auth-actions/reset-password/reset-password.component.ts + 23 + + Reset Password + + + Your password reset request has expired, please request a new one + + packages/ui/feature-authentication/src/lib/pages/auth-actions/reset-password/reset-password.component.ts + 60 + + Your password reset request has expired, please request a new one + + + + + + + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html + 9,13 + + + + + + + + + Your email has been verified. You will be redirected to sign in + + + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html + 13,17 + + + Your email has been verified. You will be redirected to sign in + + + + + + This invitation has expired, redirecting to sign in... + + + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html + 17,19 + + + This invitation has expired, redirecting to sign in... + + + + + Sign in + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html + 20 + + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 52 + + Sign in + + + Verifying Email + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 21 + + Verifying Email + + + Verified Email + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 22 + + Verified Email + + + Verification Failed + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 23 + + Verification Failed + + + Verifying email + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 38 + + Verifying email + + + Email Verified + + packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts + 61 + + Email Verified + + + Forgot Password + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 3 + + Forgot Password + + + If the user exists we'll send you an email with a link to reset your password. + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 4,6 + + If the user exists we'll send you an email with a link to reset your password. + + + + Email is required Email is invalid + + + + + + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 12,20 + + + Email is required Email is invalid + + + + + + + + Send email + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 25,27 + + Send email + + + Back to sign in + + packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html + 31 + + Back to sign in + + + Welcome Back + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 1 + + Welcome Back + + + Email is invalid + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 8,10 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 33,35 + + Email is invalid + + + Email is required + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 11 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 39,40 + + Email is required + + + Password + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 15 + + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 16 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 47 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 49 + + Password + + + Password is required + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 17 + + Password is required + + + Invalid email or password + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 19 + + Invalid email or password + + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + You are not invited to any project, please contact your administrator. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Your user is inactive, please contact your administrator to activate it. + + + Sign in + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 36,38 + + Sign in + + + Don't have an account? + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 43 + + Don't have an account? + + + Sign up now + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 43 + + Sign up now + + + Forgot password? + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 47 + + Forgot password? + + + Let's Get Started! + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 3,5 + + Let's Get Started! + + + First Name + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 10 + + First Name + + + First name is required + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 12,14 + + First name is required + + + Last Name + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 17 + + Last Name + + + Last name is required + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 19,21 + + Last name is required + + + Email is used + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 36,38 + + Email is used + + + Password is required + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 51,53 + + Password is required + + + Password is invalid + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 54 + + Password is invalid + + + Lowercase + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 71 + + Lowercase + + + Uppercase + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 81 + + Uppercase + + + Special Character + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 92 + + Special Character + + + Number + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 101 + + Number + + + 8-64 Characters + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 114 + + 8-64 Characters + + + Receive updates and newsletters from activepieces + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 120,123 + + Receive updates and newsletters from activepieces + + + Sign up + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 126,128 + + Sign up + + + By creating an account, you agree to our + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 134 + + By creating an account, you agree to our + + + terms of service + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 136,137 + + terms of service + + + and + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 138 + + and + + + privacy policy + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 139 + + privacy policy + + + Already have an account? + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 143 + + Already have an account? + + + Signing up is disabled. + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 150 + + Signing up is disabled. + + + If you are the owner, please refer to the configuration section in the documentation to learn how to enable it. + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 151,154 + + If you are the owner, please refer to the configuration section in the documentation to learn how to enable it. + + + Sign up + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 60 + + Sign up + + + Verify email + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 68 + + Verify email + + + Reset password + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 78 + + Reset password + + + Forgot password + + packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts + 88 + + Forgot password + + + True + + packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts + 71 + + True + + + False + + packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts + 71 + + False + + + Delete Step + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/delete-flow-item-action/delete-flow-item-action.component.html + 1 + + Delete Step + + + Duplicate Step + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/duplicate-step-action/duplicate-step-action.component.html + 1 + + Duplicate Step + + + Change Trigger + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/replace-trigger-action/replace-trigger-action.component.html + 1 + + Change Trigger + + + Can't move here + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/add-button-core.component.ts + 21 + + Can't move here + + + Incomplete settings + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.html + 35 + + Incomplete settings + + + Loop + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 197 + + Loop + + + Choose a trigger + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 207 + + Choose a trigger + + + Webhook trigger + + packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts + 209 + + Webhook trigger + + + End + + + packages/ui/feature-builder-canvas/src/lib/components/widgets/end-of-flow-widget/end-of-flow-widget.component.html + 2,4 + + End + + + + + Complete step + + + + + packages/ui/feature-builder-canvas/src/lib/components/widgets/incomplete-steps-widget/incomplete-steps-widget.component.html + 5,7 + + + Complete step + + + + + + + + + + packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.html + 11,13 + + + + + + + Test flow + + packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts + 52 + + Test flow + + + Saving... + + packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts + 53 + + Saving... + + + View Only + + + packages/ui/feature-builder-canvas/src/lib/components/widgets/view-only-mode-widget/view-only-mode-widget.component.html + 2,4 + + View Only + + + + + Value + + + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 5 + + + Value + + + + + First value is required + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 9,11 + + First value is required + + + Condition + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 32 + + Condition + + + Condition is required + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 38,40 + + Condition is required + + + Second Value + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 47 + + Second Value + + + Second value is required + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 51,53 + + Second value is required + + + Case sensitive + + packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html + 64,66 + + Case sensitive + + + And + + packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html + 11,13 + + And + + + + And + + packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html + 21,23 + + + And + + + + Or + + packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html + 25,27 + + + Or + + + Add NPM Package + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 1 + + Add NPM Package + + + + Please type the name of an NPM package. + + + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 3,6 + + + Please type the name of an NPM package. + + + + + Package name is required + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 11,13 + + Package name is required + + + Package not found + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 15,17 + + Package not found + + + The latest version will be fetched and added to package.json + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 21,23 + + The latest version will be fetched and added to package.json + + + Add + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html + 31,33 + + Add + + + Code Editor + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 3 + + Code Editor + + + Add npm package + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 6,8 + + Add npm package + + + Test code + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 10,11 + + Test code + + + Minimize Editor + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 13 + + Minimize Editor + + + Output + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 74,76 + + Output + + + Console + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html + 81,83 + + Console + + + Copied to clipboard + + packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts + 198 + + Copied to clipboard + + + Test Step First + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html + 42,44 + + Test Step First + + + This step needs to be tested in order to view its data. + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html + 45,47 + + This step needs to be tested in order to view its data. + + + Go to step + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html + 50,52 + + Go to step + + + + Describe the path to the property using JSON format + + + + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 4,6 + + + Describe the path to the property using JSON format + + + + + + + Path is invalid + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 12,14 + + Path is invalid + + + + Path must begin with + + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html + 16,18 + + + Path must begin with + + + + + Select + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/generic-mention-item/generic-mention-item.component.html + 4,5 + + Select + + + + result: + + + + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html + 37,40 + + + result: + + + + + + + Insert + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html + 43,44 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/step-mentions-tree/step-mentions-tree.component.html + 19,20 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/step-mentions-tree/step-mentions-tree.component.html + 44,45 + + Insert + + + This trigger needs to have data loaded from your account, to use as sample data + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html + 56,58 + + This trigger needs to have data loaded from your account, to use as sample data + + + Go to trigger + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html + 61,62 + + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html + 33,34 + + Go to trigger + + + Send sample data first + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html + 25,27 + + Send sample data first + + + This trigger needs to have data sent to it, to use as sample data + + packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html + 28,30 + + This trigger needs to have data sent to it, to use as sample data + + + Support + + packages/ui/feature-builder-header/src/lib/feedback/support.component.html + 6 + + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 37 + + Support + + + Dashboard + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 14 + + Dashboard + + + Rename + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 43 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 57 + + Rename + + + Duplicate + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 50 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 77 + + Duplicate + + + Import + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 56 + + Import + + + Export + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 63 + + Export + + + Delete + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html + 72 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 84 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 64 + + Delete + + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + + + This will permanently delete the flow, all its data and any background runs. + You can't undo this action. + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 141,142 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 119,120 + + This will permanently delete the flow, all its data and any background runs. + You can't undo this action. + + + Import Flow + + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 1,3 + + Import Flow + + + + + Important: Importing a flow will overwrite your current flow. + + + + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 9 + + + Important: Importing a flow will overwrite your current flow. + + + + + + File + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 12 + + File + + + Import + + packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html + 26,28 + + Import + + + Edit Flow + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.html + 16,18 + + Edit Flow + + + Add 1 more step to publish + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 86 + + Add 1 more step to publish + + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + Published + + + Your flow has invalid steps + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 90 + + Your flow has invalid steps + + + Publish Flow + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 92 + + Publish Flow + + + Saving + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 98 + + Saving + + + Publishing + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 100 + + Publishing + + + Publish + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 102 + + Publish + + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + + + Run Details + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/run-details.component.html + 2 + + Run Details + + + No details available for this run. + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/run-details.component.html + 49 + + No details available for this run. + + + Step succeeded + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 10 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 30 + + Step succeeded + + + Step paused + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 14 + + Step paused + + + Duration: + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 22 + + Duration: + + + Output + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html + 27 + + Output + + + Done + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/steps-results-list/step-result.component.html + 45,47 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 27,29 + + Done + + + All Iterations + + packages/ui/feature-builder-left-sidebar/src/lib/run-details/steps-results-list/step-result.component.html + 72,74 + + All Iterations + + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Draft + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> + In that case, you will also have to add an <b>HTTP step</b> with return response to the end of your flow.<br> <br> + **If the flow takes more than 30 seconds, it will give a 408 Request Timeout response.**<br> + + + + + + packages/ui/feature-builder-right-sidebar/src/lib/edit-step-sidebar/edit-step-form-container/edit-step-form-container.component.html + 17,21 + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> + In that case, you will also have to add an <b>HTTP step</b> with return response to the end of your flow.<br> <br> + **If the flow takes more than 30 seconds, it will give a 408 Request Timeout response.**<br> + + + + + + + Edit step name + + packages/ui/feature-builder-right-sidebar/src/lib/edit-step-sidebar/step-name-editor/step-name-editor.component.html + 6 + + Edit step name + + + Or + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/branch-step-input-form/branch-step-input-form.component.html + 11,13 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 19,21 + + Or + + + Inputs + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/code-step-input-form/code-step-input-form.component.html + 4 + + Inputs + + + Items + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/loop-step-input-form/loop-step-input-form.component.html + 4 + + Items + + + Items are required. + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/loop-step-input-form/loop-step-input-form.component.html + 7,9 + + Items are required. + + + Action + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html + 4 + + Action + + + No options available + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html + 23,25 + + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 40,42 + + No options available + + + Action is required + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html + 27,29 + + Action is required + + + Trigger + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 4 + + Trigger + + + Select a Trigger + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 5 + + Select a Trigger + + + Instantly triggered once change occurs + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 21 + + Instantly triggered once change occurs + + + Checks for changes periodically, based on your plan + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 29 + + Checks for changes periodically, based on your plan + + + Trigger is required + + packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html + 44,46 + + Trigger is required + + + Request Piece + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-list/step-type-list.component.html + 5,7 + + Request Piece + + + Search + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.html + 7 + + + packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html + 10 + + Search + + + Select Trigger + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 73 + + Select Trigger + + + Select Step + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 75 + + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 80 + + Select Step + + + All + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 166 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 7 + + All + + + Oops! We didn't find any results. + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 168 + + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 174 + + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 182 + + Oops! We didn't find any results. + + + Core + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 172 + + Core + + + App Events + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 179 + + App Events + + + App Actions + + packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts + 180 + + App Actions + + + Uncategorized + + packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts + 112 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html + 21 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.datasource.ts + 102 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 26 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 11 + + + packages/ui/feature-dashboard/src/lib/store/folders/folders.effects.ts + 22 + + Uncategorized + + + + Run succeeded () + + + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 7 + + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 11 + + + Run succeeded () + + + + + + Run failed () + + + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 15 + + + Run failed () + + + + + Flow is running + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 19 + + Flow is running + + + Flow is paused ... + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 23 + + Flow is paused ... + + + + Run exceeded seconds, try to optimize your steps. + + + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 27,28 + + + Run exceeded seconds, try to optimize your steps. + + + + + Run failed for an unknown reason, contact support. + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 32 + + Run failed for an unknown reason, contact support. + + + Exit Run + + packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html + 39 + + Exit Run + + + Generate Sample Data + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 2,4 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 2,4 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 2,4 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 2,4 + + Generate Sample Data + + + Test step + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 40,41 + + Test step + + + Tested Successfully + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 60 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 31 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 24 + + Tested Successfully + + + Testing Failed + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 63 + + Testing Failed + + + Retest + + packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html + 72 + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 40 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 30 + + Retest + + + Test this trigger to generate sample data that can be used in the next steps + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 10 + + Test this trigger to generate sample data that can be used in the next steps + + + Test Trigger + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 16,17 + + Test Trigger + + + Use Mock Data + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 22,23 + + Use Mock Data + + + Data + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 44,46 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 40,42 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 34,36 + + Data + + + + (Result ) + + + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 49 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 45 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 39 + + + (Result ) + + + + + Testing trigger + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 64 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 54 + + Testing trigger + + + Cancel + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 67 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 57 + + Cancel + + + Action Required + + packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html + 73 + + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 63 + + Action Required + + + Load sample data from your account to be used in the next steps + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 13 + + Load sample data from your account to be used in the next steps + + + Load data + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 18 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 36 + + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 69 + + Load data + + + This sample data can be used in the next steps + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 49,51 + + This sample data can be used in the next steps + + + No results found + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 60 + + No results found + + + Please add some data then press "Load Data" + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 72 + + Please add some data then press "Load Data" + + + Testing data failed. Please ensure that the settings are correct or try again. + + packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html + 73,74 + + Testing data failed. Please ensure that the settings are correct or try again. + + + Send a request to generate sample data that can be used in the next steps + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 10 + + Send a request to generate sample data that can be used in the next steps + + + Send Data + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 13,15 + + Send Data + + + Please send data to the webhook endpoint to test the step. + + packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html + 65,68 + + Please send data to the webhook endpoint to test the step. + + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + New Connection + + + Name is required + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 20,22 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 15,17 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 18,20 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 19,21 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 19,21 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 14,16 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 14,16 + + Name is required + + + Name can only contain letters, numbers and underscores + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 24,26 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 19,21 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 22,24 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 23,25 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 23,25 + + Name can only contain letters, numbers and underscores + + + Name is already used + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 27,29 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 22,24 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 25,27 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 26,28 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 26,28 + + Name is already used + + + is required + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 38 + + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 47 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 35 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 43 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 56 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 36 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 48 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 60 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 73 + + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 37 + + is required + + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Reconnect + + + You have to connect an account to continue + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 58,60 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 86,88 + + You have to connect an account to continue + + + I would like to use my own app credentials + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 64,67 + + I would like to use my own app credentials + + + The ID of this connection definition. You will need to select this key whenever you want to reuse this connection. + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.ts + 75 + + The ID of this connection definition. You will need to select this key whenever you want to reuse this connection. + + + Redirect URL is required + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 37,38 + + Redirect URL is required + + + Client ID + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 42 + + Client ID + + + Client ID is required + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 44,45 + + Client ID is required + + + Client Secret + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 48 + + Client Secret + + + Client Secret is required + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 51,52 + + Client Secret is required + + + I would like to use predefined app credentials + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 92,93 + + I would like to use predefined app credentials + + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html + 2,4 + + New Connection + + + Connect + + packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html + 2 + + Connect + + + Disconnect + + packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html + 4 + + Disconnect + + + Please make sure this connections's credentials are correct. + + + packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html + 5,7 + + Please make sure this connections's credentials are correct. + + + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Home + + + Ask the Community + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 29 + + Ask the Community + + + Documentation + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 39 + + Documentation + + + Docs + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 47 + + Docs + + + Flows + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 92 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 69 + + Flows + + + Runs + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 101 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 29 + + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 5 + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 3 + + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 9 + + Runs + + + Connections + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 107 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 61 + + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts + 35 + + Connections + + + Team + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 113 + + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 45 + + Team + + + What's new + + packages/ui/feature-dashboard/src/lib/dashboard-container.component.html + 15,17 + + What's new + + + Plans + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 37 + + Plans + + + Platform + + packages/ui/feature-dashboard/src/lib/dashboard.routing.ts + 81 + + Platform + + + App + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 7 + + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 11 + + App + + + Delete Connection + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 43 + + Delete Connection + + + No connections created yet + + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 59,61 + + No connections created yet + + + Create Your First Flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 5 + + Create Your First Flow + + + Demo flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 15 + + Demo flow + + + Learn from our Gelato maker demo flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 16,18 + + Learn from our Gelato maker demo flow + + + Build flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 37 + + Build flow + + + Start building your first flow from scratch + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html + 38,40 + + Start building your first flow from scratch + + + Untitled + + packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.ts + 41 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts + 41 + + Untitled + + + All flows + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html + 6 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 15 + + All flows + + + + New Flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html + 26 + + + New Flow + + + My Flows + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 4 + + My Flows + + + Steps + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 21 + + Steps + + + Folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 54 + + Folder + + + Move to... + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 70 + + Move to... + + + You don't have any flows yet + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 101 + + You don't have any flows yet + + + This folder doesn't have any flows yet + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html + 104 + + This folder doesn't have any flows yet + + + Real time flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 193 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 200 + + Real time flow + + + + Runs + + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 197,199 + + + Runs + + + + + Please contact support as your published flow has a problem + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 207 + + Please contact support as your published flow has a problem + + + Please publish the flow + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 234 + + Please publish the flow + + + Flow is on + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 238 + + Flow is on + + + Flow is off + + packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts + 239 + + Flow is off + + + Folders + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 2 + + Folders + + + New Folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html + 7 + + New Folder + + + Ascending + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.ts + 140 + + Ascending + + + Descending + + packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.ts + 143 + + Descending + + + New Folder + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 1,2 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 1,2 + + New Folder + + + + Name your new folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 4,6 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 4,6 + + Name your new folder + + + Select Folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 9 + + Select Folder + + + No folders to move to + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 17 + + No folders to move to + + + Must select a folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 19 + + Must select a folder + + + Move + + packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html + 29,31 + + Move + + + Name is already used + + packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html + 17 + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 17 + + Name is already used + + + + Rename Folder () + + + + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 1,2 + + + Rename Folder () + + + + + + Change the name of your folder + + packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html + 4,6 + + Change the name of your folder + + + Filter by status + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 5 + + Filter by status + + + Send email notifications about failed runs + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 14,16 + + Send email notifications about failed runs + + + Flow + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 20 + + Flow + + + Started + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 25 + + Started + + + Finished + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 30 + + Finished + + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + + + No runs instantiated yet + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 72,74 + + No runs instantiated yet + + + This is where you can install pieces made by yourself or contributors. + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 4,6 + + This is where you can install pieces made by yourself or contributors. + + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Add Piece + + + No pieces installed + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 49,51 + + No pieces installed + + + Install Piece + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 1 + + Install Piece + + + Piece name + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 7 + + Piece name + + + NPM package name + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 8 + + NPM package name + + + This is required + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 11,13 + + This is required + + + Install + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html + 61,63 + + Install + + + + Use this to install a <a href="https://www.activepieces.com/docs/developers/building-pieces/create-action" target="_blank" rel="noopener">custom piece</a> that you (or someone else) created. + Once the piece is installed, you can use it in the flow builder. + <br><br>**Warning:** + Make sure you trust the author as the piece will have access to your flow data and it might not be compatible with the current version of Activepieces. + + + packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.ts + 35,40 + + + Use this to install a <a href="https://www.activepieces.com/docs/developers/building-pieces/create-action" target="_blank" rel="noopener">custom piece</a> that you (or someone else) created. + Once the piece is installed, you can use it in the flow builder. + <br><br>**Warning:** + Make sure you trust the author as the piece will have access to your flow data and it might not be compatible with the current version of Activepieces. + + + + Learn about this flow + + packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html + 3,5 + + Learn about this flow + + + You used a template for this flow and you can read our guide on how to set it up. + + packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html + 6,8 + + You used a template for this flow and you can read our guide on how to set it up. + + + Go to guide + + packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html + 12 + + Go to guide + + + Ignore + + packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html + 18,20 + + Ignore + + + Use Template + + packages/ui/feature-templates/src/lib/template-card/template-card.component.html + 8,10 + + Use Template + + + Apps + + + packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html + 1,3 + + Apps + + + + Select + + packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html + 6 + + Select + + + Browse Templates + + packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html + 3 + + Browse Templates + + + Search templates + + packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html + 34 + + Search templates + + + Start from scratch + + packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html + 47 + + Start from scratch + + + Filters + + packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html + 2,4 + + Filters + + + + diff --git a/packages/ui/core/src/locale/messages.hu.xlf b/packages/ui/core/src/locale/messages.hu.xlf index da6bd3672..953396060 100644 --- a/packages/ui/core/src/locale/messages.hu.xlf +++ b/packages/ui/core/src/locale/messages.hu.xlf @@ -2,7 +2,7 @@ - + You have reached the limits of your current plan. โญ to unlock more features. @@ -13,78 +13,62 @@ packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.html 17,20 - Elรฉrted a jelenlegi csomagod korlรกtait. โญ a tovรกbbi funkciรณkhoz valรณ hozzรกfรฉrรฉshez. + Elรฉrted a jelenlegi csomagod korlรกtait. โญ a tovรกbbi funkciรณkhoz valรณ hozzรกfรฉrรฉshez. - + Please contact your platform admin packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.ts 26 - Kรฉrlek, lรฉpj kapcsolatba a platform adminisztrรกtoroddal + Kรฉrlek, lรฉpj kapcsolatba a platform adminisztrรกtoroddal - + Upgrade packages/ee/billing/ui/src/lib/upgrade-dialog/upgrade-dialog.component.ts 27 - Upgrade + Frissรญtรฉs - + Share template packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html 8 - Share template + Megosztรกsi forma - + Description packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html 18 - Description + Leรญrรกs - + Blog Url packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html 24 - Blog Url + Blog Link - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - Image url - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Featured Description - - + Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 - Filters + Szลฑrล‘k - + Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -110,6 +94,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -146,13 +134,13 @@ packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html 58,60 - Cancel + Mรฉgse - + Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -162,91 +150,95 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 - Confirm + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + + Megerล‘sรญtรฉs - + OR packages/ee/components/src/lib/third-party-auth/third-party-auth.component.html 29,31 - OR + VAGY - + Sign up with packages/ee/components/src/lib/third-party-auth/third-party-auth.component.ts 21 - Sign up with + Regisztrรกlj a - + Sign in with packages/ee/components/src/lib/third-party-auth/third-party-auth.component.ts 22 - Sign in with + Jelentkezz be a - + Team Invitation Accepted packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html 3,5 - Team Invitation Accepted + Csapat meghรญvรกs elfogadva - + Thank you for accepting the invitation. We are redirecting you right now... packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html 6,8 - Thank you for accepting the invitation. We are redirecting you right now... + Kรถszรถnjรผk, hogy elfogadta a meghรญvรกst. Most รกtirรกnyรญtjuk... - + Invalid invitation token. Please try again. packages/ee/project-members/src/lib/accept-invitation/accept-invitation.component.html 11,13 - Invalid invitation token. Please try again. + ร‰rvรฉnytelen meghรญvรณkรณd. Kรฉrjรผk, prรณbรกlja รบjra. - + Invite Project Member packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html 1,2 - Invite Project Member + Projekttagok meghรญvรกsa - + Type the email address of the user you want to invite. packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html 5,7 - Type the email address of the user you want to invite. + รrja be azon felhasznรกlรณ e-mail cรญmรฉt, akit meghรญvni szeretne. - + The invited email already has an account. Please contact support for assistance. packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html 13 - The invited email already has an account. Please contact support for assistance. + A meghรญvott e-mail cรญmhez mรกr tartozik egy fiรณk. Kรฉrjรผk, lรฉpjen kapcsolatba a tรกmogatรกssal segรญtsรฉgรฉrt. - + Email is Required packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html 16 - Email is Required + Email cรญm kรถtelezล‘ - + Role packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -256,57 +248,57 @@ packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html 19 - Role + Szerepkรถr - + Invite packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html 37,39 - Invite + Meghรญv - + Internal error occurred please contact support packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts 65 - Internal error occurred please contact support + Belsล‘ hiba tรถrtรฉnt, kรฉrjรผk, forduljon az รผgyfรฉlszolgรกlathoz - + ADMIN packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts 96 - ADMIN + ADMIN - + EDITOR packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts 98 - EDITOR + Szerkesztล‘ - + VIEWER packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts 100 - VIEWER + Nรฉzล‘ - + UNKNOWN packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.ts 102 - UNKNOWN + ISMERETLEN - + Project Members packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html @@ -316,9 +308,9 @@ packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts 44 - Project Members + Projekt tagok - + Email packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html @@ -336,9 +328,9 @@ packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 27 - Email + E-mail - + Status packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html @@ -356,33 +348,33 @@ packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 34 - Status + Stรกtusz - + Invited At packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html 24 - Invited At + Meghรญvva - + + Invite packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html 31 - + Invite + + Meghรญv - + Delete Invitation packages/ee/project-members/src/lib/project-members-table/project-members-table.component.html 36 - Delete Invitation + Meghรญvรณ tรถrlรฉse - + Active packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts @@ -392,17 +384,17 @@ packages/ui/common/src/lib/components/status-icon/state-icon.component.ts 88 - Active + Aktรญv - + Pending packages/ee/project-members/src/lib/project-members-table/project-members-table.component.ts 143 - Pending + Fรผggล‘ben levล‘ - + Name packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -440,9 +432,9 @@ packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html 17 - Name + Nรฉv - + Created packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -452,37 +444,21 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 27 - Created + Lรฉtrehozva - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbots - - + Create packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html 27,29 - Create + Lรฉtrehoz - + Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -500,9 +476,9 @@ packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html 6 - Edit + Szerkesztรฉs - + Save packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -524,57 +500,89 @@ packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html 51,53 - Save + Mentรฉs - + Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 - Projects + Projektek - + Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 - Appearance + Megjelenรฉs - + Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 + + Elemek + + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 - Pieces + Sablonok - + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 + + Beรกllรญtรกsok + + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 - Settings + Felhasznรกlรณk - + Version packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html 22 - Version + Verziรณ - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - Add Piece + Frissรญtve - + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Elem hozzรกadรกsa + + Delete @@ -584,13 +592,13 @@ packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html 1,2 - - Delete + + Tรถrlรฉs - + Type DELETE and press confirm to continue. @@ -600,45 +608,45 @@ packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html 7,9 - - Type DELETE and press confirm to continue. + + "Irja be a Tร–RLร‰S szรณt, majd nyomja meg a megerล‘sรญtรฉshez." - + Please write the confirmation message correctly packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html 14,16 - Please write the confirmation message correctly + Irja be a Tรถrlรฉs szรณt, majd kattintson a megerล‘sรญtรฉshez - + Close packages/ui/common/src/lib/components/json-view/json-view-dialog/json-view-dialog.component.html 13,15 - Close + Bezรกrรกs - + BETA packages/ui/common/src/lib/components/page-title/page-title.component.html 6 - BETA + Bร‰TA - + Page Size: packages/ui/common/src/lib/components/pagination/ap-paginator.component.html 2 - Page Size: + Lapmรฉret: - + Succeeded packages/ui/common/src/lib/components/status-icon/state-icon.component.ts @@ -648,25 +656,25 @@ packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 10 - Succeeded + Sikerรผlt - + Internal Error packages/ui/common/src/lib/components/status-icon/state-icon.component.ts 75 - Internal Error + Belsล‘ hiba - + Timed Out packages/ui/common/src/lib/components/status-icon/state-icon.component.ts 77 - Timed Out + Idล‘tรบllรฉpรฉs - + Failed packages/ui/common/src/lib/components/status-icon/state-icon.component.ts @@ -676,17 +684,17 @@ packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 9 - Failed + Sikertelen - + Paused packages/ui/common/src/lib/components/status-icon/state-icon.component.ts 83 - Paused + Szรผnetel - + Running packages/ui/common/src/lib/components/status-icon/state-icon.component.ts @@ -696,17 +704,17 @@ packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 8 - Running + Futรณ - + Error packages/ui/common/src/lib/components/status-icon/state-icon.component.ts 90 - Error + Hiba - + My Pieces packages/ui/common/src/lib/components/user-avatar/user-avatar.component.html @@ -714,55 +722,55 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html 3 - My Pieces + Sajรกt elemek - + Logout packages/ui/common/src/lib/components/user-avatar/user-avatar.component.html 74 - Logout + Kijelentkezรฉs - + An unexpected error occured, please contact support packages/ui/common/src/lib/utils/consts.ts 1 - An unexpected error occured, please contact support + Belsล‘ hiba tรถrtรฉnt, kรฉrjรผk, forduljon az รผgyfรฉlszolgรกlathoz - + Webhook Trigger packages/ui/common/src/lib/utils/utils.ts 6 - Webhook Trigger + Webhook Trigger - + Empty Trigger packages/ui/common/src/lib/utils/utils.ts 10 - Empty Trigger + รœres Trigger - + Trigger packages/ui/common/src/lib/utils/utils.ts 13 - Trigger + Trigger - + Code packages/ui/common/src/lib/utils/utils.ts @@ -776,17 +784,17 @@ packages/ui/feature-builder-right-sidebar/src/lib/input-forms/code-step-input-form/code-step-input-form.component.html 12 - Code + Kรณd - + Loop on Items packages/ui/common/src/lib/utils/utils.ts 25 - Loop on Items + Elemeken valรณ vรฉgiglรฉpรฉs - + Branch packages/ui/common/src/lib/utils/utils.ts @@ -796,97 +804,89 @@ packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts 193 - Branch + รg - + Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 - - Import Flow - - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 + 25 - Chatbot + Folyamat importรกlรกsa - + Upgrade packages/ui/core/src/app/app.component.html 7,8 - Upgrade + Frissรญtรฉs - + Ignore packages/ui/core/src/app/app.component.html 9,10 - Ignore + Mellล‘zรฉs - + Steps in this flow packages/ui/core/src/app/modules/import-flow/import-flow.component.html 17 - Steps in this flow + A folyamatban talรกlhatรณ lรฉpรฉsek - + Template Description packages/ui/core/src/app/modules/import-flow/import-flow.component.html 24 - Template Description + Sablon leรญrรกsa - + Use Template packages/ui/core/src/app/modules/import-flow/import-flow.component.html 36,37 - Use Template + Sablon hasznรกlata - + Page not found ๐Ÿ˜ข packages/ui/core/src/app/modules/not-found/not-found.component.html 5 - Page not found ๐Ÿ˜ข + Az oldal nem talรกlhatรณ ๐Ÿ˜ข - + Looks like you are lost. packages/ui/core/src/app/modules/not-found/not-found.component.html 6 - Looks like you are lost. + รšgy tลฑnik, eltรฉvedtรฉl. - + Return Home packages/ui/core/src/app/modules/not-found/not-found.component.html 7 - Return Home + Vissza a kezdล‘lapra - + redirect-url works! packages/ui/core/src/app/modules/redirect-url/redirect-url.component.html 1 - redirect-url works! + az รกtirรกnyรญtรกs-url mลฑkรถdik! - + Didn't receive an email? Resend @@ -896,69 +896,69 @@ packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.html 14,19 - - Didn't receive an email? Resend + + Nem kapott e-mailt? รšjrakรผldรฉs - + Go back to sign in packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.html 19 - Go back to sign in + Vissza a belรฉpรฉshez - + We sent you a link to complete your registration, check your email. packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts 29 - We sent you a link to complete your registration, check your email. + Elkรผldtรผnk ร–nnek egy linket a regisztrรกciรณ befejezรฉsรฉhez, ellenล‘rizze az e-mailjรฉt. - + We sent you a link to reset your password, check your email. packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts 30 - We sent you a link to reset your password, check your email. + Elkรผldtรผnk ร–nnek egy linket a jelszรณ visszaรกllรญtรกsรกhoz, ellenล‘rizze az e-mailjรฉt. - + Verification resent packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts 51 - Verification resent + Az ellenล‘rzรฉs รบjra elkรผldve - + Password reset link resent packages/ui/feature-authentication/src/lib/components/send-email-for-auth-action/send-email-for-auth-action.component.ts 75 - Password reset link resent + Jelszรณ-visszaรกllรญtรกsi hivatkozรกs รบjrakรผldve - + Reset Password packages/ui/feature-authentication/src/lib/pages/auth-actions/reset-password/reset-password.component.ts 23 - Reset Password + Jelszรณ visszaรกllรญtรกsa - + Your password reset request has expired, please request a new one packages/ui/feature-authentication/src/lib/pages/auth-actions/reset-password/reset-password.component.ts 60 - Your password reset request has expired, please request a new one + Jelszรณ-visszaรกllรญtรกsi kรฉrelme lejรกrt, kรฉrjen รบjat - + @@ -967,12 +967,12 @@ packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html 9,13 - + - + Your email has been verified. You will be redirected to sign in @@ -981,12 +981,12 @@ packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html 13,17 - - Your email has been verified. You will be redirected to sign in + + E-mail-cรญmรฉt ellenล‘riztรผk. A rendszer รกtirรกnyรญtja a bejelentkezรฉshez - + This invitation has expired, redirecting to sign in... @@ -995,12 +995,12 @@ packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html 17,19 - - This invitation has expired, redirecting to sign in... + + Ez a meghรญvรณ lejรกrt, รกtirรกnyรญtรกs a bejelentkezรฉshez... - + Sign in packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.html @@ -1010,65 +1010,65 @@ packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts 52 - Sign in + Belรฉpรฉs - + Verifying Email packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts 21 - Verifying Email + E-mail cรญm megerล‘sรญtรฉse - + Verified Email packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts 22 - Verified Email + Hitelesรญtett email - + Verification Failed packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts 23 - Verification Failed + Ellenล‘rzรฉs sikertelen - + Verifying email packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts 38 - Verifying email + E-mail cรญm megerล‘sรญtรฉse - + Email Verified packages/ui/feature-authentication/src/lib/pages/auth-actions/verify-email-post-sign-up/verify-email-post-sign-up.component.ts 61 - Email Verified + Ellenล‘rzรถtt email-cรญm - + Forgot Password packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html 3 - Forgot Password + Elfelejtette a jelszavรกt - + If the user exists we'll send you an email with a link to reset your password. packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html 4,6 - If the user exists we'll send you an email with a link to reset your password. + Ha a felhasznรกlรณ lรฉtezik, elkรผldรผnk ร–nnek egy e-mailt egy linkkel a jelszรณ visszaรกllรญtรกsรกhoz. - + Email is required Email is invalid @@ -1080,39 +1080,39 @@ packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html 12,20 - - Email is required Email is invalid + + E-mail megadรกsa kรถtelezล‘. Az e-mail-cรญm รฉrvรฉnytelen - + Send email packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html 25,27 - Send email + E-mail kรผldรฉse - + Back to sign in packages/ui/feature-authentication/src/lib/pages/forgot-password/forgot-password.component.html 31 - Back to sign in + Vissza a bejelentkezรฉshez - + Welcome Back packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html 1 - Welcome Back + รœdv รบjra - + Email is invalid packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html @@ -1122,9 +1122,9 @@ packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 33,35 - Email is invalid + ร‰rvรฉnytelen email cรญm - + Email is required packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html @@ -1134,9 +1134,9 @@ packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 39,40 - Email is required + Email cรญm kรถtelezล‘ - + Password packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html @@ -1154,363 +1154,375 @@ packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 49 - Password + Jelszรณ - + Password is required packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html 17 - Password is required + Jelszรณ megadรกsa kรถtelezล‘ - + Invalid email or password packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html 19 - Invalid email or password + ร‰rvรฉnytelen e-mail cรญm vagy jelszรณ - + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + Nem vagy meghรญvva egyetlen projektbe sem, kรฉrjรผk, lรฉpjen kapcsolatba az adminisztrรกtorรกval. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + A felhasznรกlรณd nem aktรญv, kรฉrlek lรฉpj kapcsolatba az adminisztrรกtoroddal az aktivรกlรกs รฉrdekรฉben. + + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 - Sign in + Belรฉpรฉs - + Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 - Don't have an account? + Nincs mรฉg fiรณkod? - + Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 - Sign up now + Regisztrรกlj most - + Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 - Forgot password? + Elfelejtette a jelszavรกt? - + Let's Get Started! packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 3,5 - Let's Get Started! + Kezdjรผk! - + First Name packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 10 - First Name + Keresztnรฉv - + First name is required packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 12,14 - First name is required + Keresztnรฉv megadรกsa kรถtelezล‘ - + Last Name packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 17 - Last Name + Vezetรฉknรฉv - + Last name is required packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 19,21 - Last name is required + Vezetรฉknรฉv megadรกsa kรถtelezล‘ - + Email is used packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 36,38 - Email is used + Email cรญm kรถtelezล‘ - + Password is required packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 51,53 - Password is required + Jelszรณ megadรกsa kรถtelezล‘ - + Password is invalid packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 54 - Password is invalid + A jelszรณ รฉrvรฉnytelen - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Contact your admin to send you an invite - - + Lowercase packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 71 - Lowercase + Kisbetลฑ - + Uppercase packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 81 - Uppercase + Nagybetลฑs - + Special Character packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 92 - Special Character + Speciรกlis karakterek - + Number packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 101 - Number + Szรกm - + 8-64 Characters packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 114 - 8-64 Characters + 8-64 karakter - + Receive updates and newsletters from activepieces packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 120,123 - Receive updates and newsletters from activepieces + Frissรญtรฉseket รฉs hรญrleveleket kaphat - + Sign up packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 126,128 - Sign up + Regisztrรกlj - + By creating an account, you agree to our packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 134 - By creating an account, you agree to our + A fiรณk lรฉtrehozรกsรกval elfogadja az alรกbbiakat - + terms of service packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 136,137 - terms of service + felhasznรกlรกsi feltรฉtelek - + and packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 138 - and + รฉs - + privacy policy packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 139 - privacy policy + adatvรฉdelmi nyilatkozat - + Already have an account? packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 143 - Already have an account? + Van mรกr fiรณkod? - + Signing up is disabled. packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 150 - Signing up is disabled. + A regisztrรกciรณ le van tiltva. - + If you are the owner, please refer to the configuration section in the documentation to learn how to enable it. packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html 151,154 - If you are the owner, please refer to the configuration section in the documentation to learn how to enable it. + Ha ร–n a tulajdonos, kรฉrjรผk, tekintse meg a dokumentรกciรณ konfigurรกciรณs rรฉszรฉt, hogy megtudja, hogyan lehet engedรฉlyezni. - + Sign up packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts 60 - Sign up + Regisztrรกciรณ - + Verify email packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts 68 - Verify email + E-mail cรญm megerล‘sรญtรฉse - + Reset password packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts 78 - Reset password + Jelszรณ visszaรกllรญtรกsa - + Forgot password packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts 88 - Forgot password + Elfelejtette a jelszavรกt - + True packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts 71 - True + Igaz - + False packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts 71 - False + Hamis - + Delete Step packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/delete-flow-item-action/delete-flow-item-action.component.html 1 - Delete Step + Lรฉpรฉs tรถrlรฉse - + Duplicate Step packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/duplicate-step-action/duplicate-step-action.component.html 1 - Duplicate Step + A lรฉpรฉs megkettล‘zรฉse - + Change Trigger packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/replace-trigger-action/replace-trigger-action.component.html 1 - Change Trigger + Folyamat mรณdosรญtรกsa - + Can't move here packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/add-button-core.component.ts 21 - Can't move here + Nem lehet ide รกthelyezni - + Incomplete settings packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.html 35 - Incomplete settings + Hiรกnyos beรกllรญtรกsok - + Loop packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts 197 - Loop + Ismรฉtlรฉs - + Choose a trigger packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts 207 - Choose a trigger + Folyamat kivรกlasztรกs - + Webhook trigger packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts 209 - Webhook trigger + Webhook Trigger - + End packages/ui/feature-builder-canvas/src/lib/components/widgets/end-of-flow-widget/end-of-flow-widget.component.html 2,4 - End + Vรฉge - + Complete step @@ -1520,13 +1532,13 @@ packages/ui/feature-builder-canvas/src/lib/components/widgets/incomplete-steps-widget/incomplete-steps-widget.component.html 5,7 - - Complete step + + Lรฉpรฉs befejezรฉse - + @@ -1534,49 +1546,37 @@ packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.html 11,13 - + - + Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 - Test flow + Teszt folyamat - + Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 - Saving... + Mentรฉs... - + View Only packages/ui/feature-builder-canvas/src/lib/components/widgets/view-only-mode-widget/view-only-mode-widget.component.html 2,4 - View Only + Csak megtekintรฉs - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Add Item - - + Value @@ -1585,92 +1585,92 @@ packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 5 - - Value + + ร‰rtรฉk - + First value is required packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 9,11 - First value is required + Keresztnรฉv megadรกsa kรถtelezล‘ - + Condition packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 32 - Condition + Feltรฉtelek - + Condition is required packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 38,40 - Condition is required + A feltรฉtel kรถtelezล‘ - + Second Value packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 47 - Second Value + Mรกsodik รฉrtรฉk - + Second value is required packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 51,53 - Second value is required + A mรกsodik รฉrtรฉk kรถtelezล‘ - + Case sensitive packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 64,66 - Case sensitive + Kis-nagybetลฑ รฉrzรฉkeny - + And packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html 11,13 - And + ร‰s - + + And packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html 21,23 - + And + + ร‰s - + + Or packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html 25,27 - + Or + + Vagy - + Add NPM Package packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 1 - Add NPM Package + NPM csomag hozzรกadรกsa - + Please type the name of an NPM package. @@ -1679,124 +1679,124 @@ packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 3,6 - - Please type the name of an NPM package. + + Kรฉrjรผk, รญrja be egy NPM csomag nevรฉt. - + Package name is required packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 11,13 - Package name is required + A csomagnรฉv megadรกsa kรถtelezล‘ - + Package not found packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 15,17 - Package not found + A csomag nem talรกlhatรณ - + The latest version will be fetched and added to package.json packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 21,23 - The latest version will be fetched and added to package.json + A rendszer lekรฉri a legรบjabb verziรณt, รฉs hozzรกadja a package.json fรกjlhoz - + Add packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 31,33 - Add + Hozzรกad - + Code Editor packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 3 - Code Editor + Kรณdszerkesztล‘ - + Add npm package packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 6,8 - Add npm package + Npm csomag hozzรกadรกsa - + Test code packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 10,11 - Test code + Teszt kรณd - + Minimize Editor packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 13 - Minimize Editor + Szerkesztล‘ minimalizรกlรกsa - + Output packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 74,76 - Output + Kimenet - + Console packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 81,83 - Console + Konzol - + Copied to clipboard packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts 198 - Copied to clipboard + Mรกsolva vรกgรณlapra - + Test Step First packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 42,44 - Test Step First + Teszt lรฉpรฉs elsล‘ - + This step needs to be tested in order to view its data. packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 45,47 - This step needs to be tested in order to view its data. + Ezt a lรฉpรฉst tesztelni kell az adatok megtekintรฉsรฉhez. - + Go to step packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 50,52 - Go to step + Menjen a lรฉpรฉsre - + Describe the path to the property using JSON format @@ -1807,22 +1807,22 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 4,6 - - Describe the path to the property using JSON format + + JSON formรกtum hasznรกlatรกval รญrja le a tulajdonsรกg elรฉrรฉsi รบtjรกt - + Path is invalid packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 12,14 - Path is invalid + Az elรฉrรฉsi รบt รฉrvรฉnytelen - + Path must begin with @@ -1831,20 +1831,20 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 16,18 - - Path must begin with + + Az รบtvonalnak ezzel kell kezdล‘dnie - + Select packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/generic-mention-item/generic-mention-item.component.html 4,5 - Select + Kivรกlasztรกs - + result: @@ -1855,14 +1855,14 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html 37,40 - - result: + + eredmรฉny: - + Insert packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html @@ -1876,17 +1876,17 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/step-mentions-tree/step-mentions-tree.component.html 44,45 - Insert + Beszรบrรกs - + This trigger needs to have data loaded from your account, to use as sample data packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html 56,58 - This trigger needs to have data loaded from your account, to use as sample data + Ehhez a kioldรณhoz adatokat kell betรถlteni a fiรณkjรกbรณl, mintaadatkรฉnt valรณ hasznรกlat cรฉljรกbรณl - + Go to trigger packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html @@ -1896,69 +1896,25 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html 33,34 - Go to trigger + Ugrรกs a Tiggerhez - + Send sample data first packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html 25,27 - Send sample data first + Elล‘szรถr kรผldje el a mintaadatokat - + This trigger needs to have data sent to it, to use as sample data packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html 28,30 - This trigger needs to have data sent to it, to use as sample data - - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Switch Version - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Draft - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - Published - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - Published + Ehhez a kioldรณhoz adatokat kell kรผldeni, mintaadatkรฉnt valรณ hasznรกlat cรฉljรกbรณl - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Draft - - + Support packages/ui/feature-builder-header/src/lib/feedback/support.component.html @@ -1968,65 +1924,61 @@ packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html 37 - Support + Tรกmogatรกs - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Home + Dashboard - + Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html 57 - Rename + รtnevezรฉs - + Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 77 - Duplicate + Duplikรกlรกs - + Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 - Import + Importรกlรกs - + Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 - Export + Exportรกlรกs - + Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2036,33 +1988,41 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html 64 - Delete + Tรถrlรฉs - + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 - This will permanently delete the flow, all its data and any background runs. - You can't undo this action. + Ez vรฉglegesen tรถrli az รกramlatot, minden adatรกt รฉs hรกttรฉrfutรกsรกt. + Ezt a mลฑveletet nem lehet visszavonni. - + Import Flow packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 1,3 - Import Flow + Folyamat importรกlรกsa - + Important: Importing a flow will overwrite your current flow. @@ -2072,101 +2032,175 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 9 - - Important: Importing a flow will overwrite your current flow. + + Fontos: Egy folyamat importรกlรกsa felรผlรญrja az aktuรกlis folyamatot. - + File packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 12 - File + Fรกjl - + Import packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 26,28 - Import + Importรกlรกs - + Edit Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.html 16,18 - Edit Flow + Folyamat szerkesztรฉse - + Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 + + Adjon hozzรก mรฉg egy lรฉpรฉst a publikรกlรกshoz + + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 - Add 1 more step to publish + Publikรกlva - + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 - Your flow has invalid steps + A folyamat รฉrvรฉnytelen lรฉpรฉseket tartalmaz - + Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 - Publish Flow + Folyamat kรถzzรฉtรฉtele - + Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 - Saving + Mentรฉs - + Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 - Publishing + Kรถzzรฉtรฉtel folyamat - + Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 + + Kรถzzรฉtรฉtel + + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Vรกltozรกsok listรกja + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 - Publish + Verziรณk - + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Hoba + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + A jelenlegi tervezet verziรณjรกt felรผlรญrjรกk a # verziรณval. + + + + + + Run Details packages/ui/feature-builder-left-sidebar/src/lib/run-details/run-details.component.html 2 - Run Details + Futtatรกs rรฉszletei - + No details available for this run. packages/ui/feature-builder-left-sidebar/src/lib/run-details/run-details.component.html 49 - No details available for this run. + Nincsenek rรฉszletek elรฉrhetล‘k ehhez a futtatรกshoz. - + Step succeeded packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html @@ -2180,33 +2214,33 @@ packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html 30 - Step succeeded + A lรฉpรฉs vรฉgrehajtรกsa sikerรผlt - + Step paused packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html 14 - Step paused + Lรฉpรฉs szรผnetel - + Duration: packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html 22 - Duration: + Idล‘tartam: - + Output packages/ui/feature-builder-left-sidebar/src/lib/run-details/selected-step-result/selected-step-result.component.html 27 - Output + Kimenet - + Done packages/ui/feature-builder-left-sidebar/src/lib/run-details/steps-results-list/step-result.component.html @@ -2216,17 +2250,65 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html 27,29 - Done + Kรฉsz - + All Iterations packages/ui/feature-builder-left-sidebar/src/lib/run-details/steps-results-list/step-result.component.html 72,74 - All Iterations + Minden iterรกciรณ + + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Megtekintรฉs + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Piszkozat - + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + Nรฉzet + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> In that case, you will also have to add an <b>HTTP step</b> with return response to the end of your flow.<br> <br> @@ -2239,24 +2321,24 @@ packages/ui/feature-builder-right-sidebar/src/lib/edit-step-sidebar/edit-step-form-container/edit-step-form-container.component.html 17,21 - - If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> - In that case, you will also have to add an <b>HTTP step</b> with return response to the end of your flow.<br> <br> - **If the flow takes more than 30 seconds, it will give a 408 Request Timeout response.**<br> + + Ha vรกlaszt vรกr a webhooktรณl, fลฑzze hozzรก a <b>/sync</b> vรฉgzล‘dรฉst az URL-hez.<br> <br> + Ebben az esetben hozzรก kell adnia egy <b>HTTP lรฉpรฉst</b> is a folyamat vรฉgรฉhez vรกlasz visszaadรกsรกhoz.<br> <br> + **Ha a folyamat tรถbb mint 30 mรกsodpercig tart, 408-as kรฉrรฉsi idล‘tรบllรฉpรฉs vรกlaszt ad.**<br> - + Edit step name packages/ui/feature-builder-right-sidebar/src/lib/edit-step-sidebar/step-name-editor/step-name-editor.component.html 6 - Edit step name + Lista nevรฉnek mรณdosรญtรกsa - + Or packages/ui/feature-builder-right-sidebar/src/lib/input-forms/branch-step-input-form/branch-step-input-form.component.html @@ -2266,41 +2348,41 @@ packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html 19,21 - Or + Vagy - + Inputs packages/ui/feature-builder-right-sidebar/src/lib/input-forms/code-step-input-form/code-step-input-form.component.html 4 - Inputs + Bemenetek - + Items packages/ui/feature-builder-right-sidebar/src/lib/input-forms/loop-step-input-form/loop-step-input-form.component.html 4 - Items + Elemek - + Items are required. packages/ui/feature-builder-right-sidebar/src/lib/input-forms/loop-step-input-form/loop-step-input-form.component.html 7,9 - Items are required. + Az elemek kรถtelezล‘ek. - + Action packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html 4 - Action + Mลฑvelet - + No options available packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html @@ -2310,65 +2392,65 @@ packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html 40,42 - No options available + Nincs vรกlasztรกsi lehetล‘sรฉg - + Action is required packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-action-input-form/piece-action-input-form.component.html 27,29 - Action is required + Mลฑvelet szรผksรฉges - + Trigger packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html 4 - Trigger + Trigger - + Select a Trigger packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html 5 - Select a Trigger + Vรกlasszon triggert - + Instantly triggered once change occurs packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html 21 - Instantly triggered once change occurs + Azonnal aktivรกlรณdik ha vรกltozรกs tรถrtรฉnik - + Checks for changes periodically, based on your plan packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html 29 - Checks for changes periodically, based on your plan + Idล‘szakosan ellenล‘rzi a vรกltozรกsokat a terve alapjรกn - + Trigger is required packages/ui/feature-builder-right-sidebar/src/lib/input-forms/piece-input-forms/piece-trigger-input-form/piece-trigger-input-form.component.html 44,46 - Trigger is required + Trigger szรผksรฉges - + Request Piece packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-list/step-type-list.component.html 5,7 - Request Piece + Kรฉrรฉs elem - + Search packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.html @@ -2378,17 +2460,17 @@ packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html 10 - Search + Keresรฉs - + Select Trigger packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts 73 - Select Trigger + Vรกlasszon triggert - + Select Step packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts @@ -2398,9 +2480,9 @@ packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts 80 - Select Step + Lรฉpรฉs kivรกlasztรกsa - + All packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts @@ -2410,9 +2492,9 @@ packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 7 - All + ร–sszes - + Oops! We didn't find any results. packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts @@ -2426,37 +2508,37 @@ packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts 182 - Oops! We didn't find any results. + Hoppรก! Nem talรกltunk eredmรฉnyeket. - + Core packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts 172 - Core + Mag - + App Events packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts 179 - App Events + App esemรฉnyek - + App Actions packages/ui/feature-builder-right-sidebar/src/lib/step-type-sidebar/step-type-sidebar.component.ts 180 - App Actions + App intรฉzkedรฉsek - + Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2478,9 +2560,9 @@ packages/ui/feature-dashboard/src/lib/store/folders/folders.effects.ts 22 - Uncategorized + Kategorizรกlatlan - + Run succeeded () @@ -2493,12 +2575,12 @@ packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html 11 - - Run succeeded () + + A futรกs sikerรผlt () - + Run failed () @@ -2507,28 +2589,28 @@ packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html 15 - - Run failed () + + A futรกs nem sikerรผlt () - + Flow is running packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html 19 - Flow is running + A folyamat fut - + Flow is paused ... packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html 23 - Flow is paused ... + A folyamat szรผnetelve van ... - + Run exceeded seconds, try to optimize your steps. @@ -2537,28 +2619,28 @@ packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html 27,28 - - Run exceeded seconds, try to optimize your steps. + + A futtatรกs meghaladta a mรกsodperceket, prรณbรกlja optimalizรกlni a lรฉpรฉseit. - + Run failed for an unknown reason, contact support. packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html 32 - Run failed for an unknown reason, contact support. + A futtatรกs ismeretlen okbรณl kifolyรณlag sikertelen volt. Vegye fel a kapcsolatot a tรกmogatรกssal. - + Exit Run packages/ui/feature-builder-store/src/lib/test-run-bar/test-run-bar.component.html 39 - Exit Run + Futรกs befejezรฉse - + Generate Sample Data packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html @@ -2576,17 +2658,17 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 2,4 - Generate Sample Data + Mintaadatok generรกlรกsa - + Test step packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html 40,41 - Test step + Teszt lรฉpรฉs - + Tested Successfully packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html @@ -2600,17 +2682,17 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 24 - Tested Successfully + Sikeresen tesztelve - + Testing Failed packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html 63 - Testing Failed + A tesztelรฉs sikertelen - + Retest packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html @@ -2624,33 +2706,33 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 30 - Retest + รšjratesztelรฉs - + Test this trigger to generate sample data that can be used in the next steps packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html 10 - Test this trigger to generate sample data that can be used in the next steps + Tesztelje ezt a Tiggert a mintaadatok generรกlรกsรกhoz, amelyeket a kรถvetkezล‘ lรฉpรฉsekben lehet hasznรกlni - + Test Trigger packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html 16,17 - Test Trigger + Teszt Trigger - + Use Mock Data packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html 22,23 - Use Mock Data + Mock adatok hasznรกlata - + Data packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html @@ -2664,9 +2746,9 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 34,36 - Data + Adat - + (Result ) @@ -2683,12 +2765,12 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 39 - - (Result ) + + (Eredmรฉny ) - + Testing trigger packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html @@ -2698,9 +2780,9 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 54 - Testing trigger + Tesztelรฉsi folyamat trigger - + Cancel packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html @@ -2710,9 +2792,9 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 57 - Cancel + Mรฉgse - + Action Required packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html @@ -2722,17 +2804,17 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 63 - Action Required + Beavatkozรกs szรผksรฉges - + Load sample data from your account to be used in the next steps packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html 13 - Load sample data from your account to be used in the next steps + Mintaadatok betรถltรฉse a fiรณkjรกbรณl a kรถvetkezล‘ lรฉpรฉsekhez - + Load data packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html @@ -2746,65 +2828,85 @@ packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html 69 - Load data + Adatbetรถltรฉs - + This sample data can be used in the next steps packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html 49,51 - This sample data can be used in the next steps + Ezen pรฉldaadatokat hasznรกlhatja a kรถvetkezล‘ lรฉpรฉsekben - + No results found packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html 60 - No results found + Nincs talรกlat - + Please add some data then press "Load Data" packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html 72 - Please add some data then press "Load Data" + Kรฉrjรผk, adjon hozzรก nรฉhรกny adatot, majd kattintson a 'Adatok betรถltรฉse' gombra - + Testing data failed. Please ensure that the settings are correct or try again. packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html 73,74 - Testing data failed. Please ensure that the settings are correct or try again. + A tesztelรฉsi adatok ellenล‘rzรฉse sikertelen volt. Kรฉrjรผk, gyล‘zล‘djรถn meg arrรณl, hogy a beรกllรญtรกsok helyesek, vagy prรณbรกlja meg รบjra. - + Send a request to generate sample data that can be used in the next steps packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 10 - Send a request to generate sample data that can be used in the next steps + Kรผldjรถn egy kรฉrรฉst a mintaadatok generรกlรกsรกhoz, amelyeket a kรถvetkezล‘ lรฉpรฉsekben lehet hasznรกlni - + Send Data packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 13,15 - Send Data + Adatok kรผldรฉse - + Please send data to the webhook endpoint to test the step. packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 65,68 - Please send data to the webhook endpoint to test the step. + Kรฉrjรผk, kรผldje el az adatokat a webhook vรฉgpontjรกra a lรฉpรฉs tesztelรฉsรฉhez. - + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + รšj kapcsolat + + Name is required packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -2834,37 +2936,9 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html 14,16 - Name is required - - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Reconnect - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - New Connection + Nรฉv megadรกsa kรถtelezล‘ - + Name can only contain letters, numbers and underscores packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -2886,9 +2960,9 @@ packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html 23,25 - Name can only contain letters, numbers and underscores + A nรฉv csak betลฑket, szรกmokat รฉs alรกhรบzรกsokat tartalmazhat - + Name is already used packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -2910,9 +2984,9 @@ packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html 26,28 - Name is already used + Mรกr hasznรกlt ez a nรฉv - + is required packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -2954,9 +3028,17 @@ packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html 37 - is required + kรถtelezล‘ - + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + รšjracsatlakozรกs + + You have to connect an account to continue packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html @@ -2966,151 +3048,159 @@ packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html 86,88 - You have to connect an account to continue + A folytatรกshoz csatlakoztatnia kell egy fiรณkot - + I would like to use my own app credentials packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html 64,67 - I would like to use my own app credentials + Sajรกt alkalmazรกs-hiteleimet szeretnรฉm hasznรกlni - + The ID of this connection definition. You will need to select this key whenever you want to reuse this connection. packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.ts 75 - The ID of this connection definition. You will need to select this key whenever you want to reuse this connection. + Ezen kapcsolatdefinรญciรณ azonosรญtรณja. Ezt a kulcsot kell kivรกlasztania, amikor รบjrahasznosรญtani szeretnรฉ ezt a kapcsolatot. - + Redirect URL is required packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html 37,38 - Redirect URL is required + รtirรกnyรญtรกsi URL szรผksรฉges - + Client ID packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html 42 - Client ID + รœgyfรฉl azonosรญtรณ - + Client ID is required packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html 44,45 - Client ID is required + รœgyfรฉl azonosรญtรณ megadรกsa kรถtelezล‘ - + Client Secret packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html 48 - Client Secret + รœgyfรฉl titkos kulcs - + Client Secret is required packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html 51,52 - Client Secret is required + รœgyfรฉl azonosรญtรณ megadรกsa kรถtelezล‘ - + I would like to use predefined app credentials packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html 92,93 - I would like to use predefined app credentials + Szeretnรฉm elล‘re meghatรกrozott alkalmazรกs-hiteleket hasznรกlni - + New Connection packages/ui/feature-connections/src/lib/dialogs/secret-text-connection-dialog/secret-text-connection-dialog.component.html 2,4 - New Connection + รšj kapcsolat - + Connect packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html 2 - Connect + Kapcsolรณdรกs - + Disconnect packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html 4 - Disconnect + Szรฉtkapcsolรกs - + Please make sure this connections's credentials are correct. packages/ui/feature-connections/src/lib/form-controls/o-auth2-connect-control/o-auth2-connect-control.component.html 5,7 - Please make sure this connections's credentials are correct. + Kรฉrjรผk, gyล‘zล‘djรถn meg rรณla, hogy ez a kapcsolat hitelei helyesek. - + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Kezdล‘lap + + Ask the Community packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html 29 - Ask the Community + Kรฉrdezd a Kรถzรถssรฉget - + Documentation packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html 39 - Documentation + Dokumentรกciรณ - + Docs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html 47 - Docs + Dokumentรกciรณ - + Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 - Flows + Folyamatok - + Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3124,73 +3214,61 @@ packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html 9 - Runs + Futtatรกsok - + Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts 35 - Connections + Kapcsolรณdรกsok - + Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 - Team + Csapat - + What's new packages/ui/feature-dashboard/src/lib/dashboard-container.component.html 15,17 - What's new + รšjdonsรกgok - + Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 - - Plans - - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 + 37 - Chatbot settings + Dรญjcsomagok - + Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 - Platform + Platform - + App packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3200,73 +3278,65 @@ packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html 11 - App - - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - Updated + Alkalmazรกs - + Delete Connection packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html 43 - Delete Connection + Kapcsolat tรถrlรฉse - + No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 - No connections created yet + Mรฉg nincsenek lรฉtrehozott kapcsolatok - + Create Your First Flow packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html 5 - Create Your First Flow + Hozza lรฉtre az elsล‘ folyamatรกt - + Demo flow packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html 15 - Demo flow + Demo folyamat - + Learn from our Gelato maker demo flow packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html 16,18 - Learn from our Gelato maker demo flow + Tanulj a Gelato kรฉszรญtล‘ bemutatรณ folyamatunkbรณl - + Build flow packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html 37 - Build flow + Folyamat kialakรญtรกsa - + Start building your first flow from scratch packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.html 38,40 - Start building your first flow from scratch + Az elsล‘ folyamat kezdรฉse a nullรกrรณl - + Untitled packages/ui/feature-dashboard/src/lib/pages/flows-table/empty-flows-table/empty-flows-table.component.ts @@ -3274,11 +3344,11 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 - Untitled + Nรฉvtelen - + All flows packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -3288,155 +3358,155 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html 15 - All flows + Minden folyamat - + + New Flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html 26 - + New Flow + + รšj folyamat - + My Flows packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 4 - My Flows + Az รฉn folyamataim - + Steps packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 21 - Steps + Lรฉpรฉsek - + Folder packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 54 - Folder + Kรถnyvtรกr - + Move to... packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 70 - Move to... + รthelyezรฉs... - + You don't have any flows yet packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 101 - You don't have any flows yet + Nincsenek folyamatok - + This folder doesn't have any flows yet packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 104 - This folder doesn't have any flows yet + Ebben a mappรกban mรฉg nincsenek folyamatok - + Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 - Real time flow + Valรณs idejลฑ folyamat - + Runs packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 - - Runs + + Futtatรกsok - + Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 - Please contact support as your published flow has a problem + Kรฉrjรผk, lรฉpjen kapcsolatba a tรกmogatรกssal, mivel az รถn kรถzzรฉtett folyamata problรฉmรกt okoz - + Please publish the flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts 234 - Please publish the flow + Kรฉrjรผk, publikรกlja a folyamatot - + Flow is on packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts 238 - Flow is on + A folyamat aktรญv - + Flow is off packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts 239 - Flow is off + A folyamat kikapcsolt - + Folders packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html 2 - Folders + Mappรกk - + New Folder packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html 7 - New Folder + รšj mappa - + Ascending packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.ts 140 - Ascending + Nรถvekvล‘ - + Descending packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.ts 143 - Descending + Csรถkkenล‘ - + New Folder @@ -3447,10 +3517,10 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html 1,2 - New Folder + รšj mappa - + Name your new folder packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html @@ -3460,41 +3530,41 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html 4,6 - Name your new folder + Nevezze el az รบj mappรกjรกt - + Select Folder packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html 9 - Select Folder + Mappa Kivรกlasztรกsa - + No folders to move to packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html 17 - No folders to move to + Nincs mappa, ahovรก รกthelyezhetล‘ lenne - + Must select a folder packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html 19 - Must select a folder + Ki kell vรกlasztania egy mappรกt - + Move packages/ui/feature-dashboard/src/lib/pages/flows-table/move-flow-to-folder-dialog/move-flow-to-folder-dialog.component.html 29,31 - Move + รthelyezรฉs - + Name is already used packages/ui/feature-dashboard/src/lib/pages/flows-table/new-folder-dialog/new-folder-dialog.component.html @@ -3504,9 +3574,9 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html 17 - Name is already used + Mรกr hasznรกlt ez a nรฉv - + Rename Folder () @@ -3516,125 +3586,145 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html 1,2 - - Rename Folder () + + Mappa รกtnevezรฉse () - + Change the name of your folder packages/ui/feature-dashboard/src/lib/pages/flows-table/rename-folder-dialog/rename-folder-dialog.component.html 4,6 - Change the name of your folder + Mappa nevรฉnek megvรกltoztatรกsa - + Filter by status packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 5 - Filter by status + Szลฑrรฉs รกllapot szerint - + Send email notifications about failed runs packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 14,16 - Send email notifications about failed runs + E-mail รฉrtesรญtรฉsek kรผldรฉse a sikertelen folyamatokrรณl - + Flow packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 20 - Flow + Folyamat - + Started packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 25 - Started + Indรญtva - + Finished packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html 30 - Finished + Kรฉsz + + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + - + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 - No runs instantiated yet + Mรฉg nincs elindรญtott folyamat - + This is where you can install pieces made by yourself or contributors. packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html 4,6 - This is where you can install pieces made by yourself or contributors. + Ez az a hely, ahol olyan elemeket telepรญthet, amelyeket รถn vagy mรกsok kรฉszรญtettek. - + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Elem hozzรกadรกsa + + No pieces installed packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html 49,51 - No pieces installed + Nincsenek telepรญtett elemek - + Install Piece packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html 1 - Install Piece + Elem telepรญtรฉse - + Piece name packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html 7 - Piece name + Elem neve - + NPM package name packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html 8 - NPM package name + NPM-csomag neve - + This is required packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html 11,13 - This is required + Ez kรถtelezล‘ - + Install packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.html 61,63 - Install + Telepรญtรฉs - + Use this to install a <a href="https://www.activepieces.com/docs/developers/building-pieces/create-action" target="_blank" rel="noopener">custom piece</a> that you (or someone else) created. Once the piece is installed, you can use it in the flow builder. @@ -3645,184 +3735,101 @@ packages/ui/feature-pieces/src/lib/install-community-piece/install-community-piece-modal.component.ts 35,40 - - Use this to install a <a href="https://www.activepieces.com/docs/developers/building-pieces/create-action" target="_blank" rel="noopener">custom piece</a> that you (or someone else) created. - Once the piece is installed, you can use it in the flow builder. - <br><br>**Warning:** - Make sure you trust the author as the piece will have access to your flow data and it might not be compatible with the current version of Activepieces. - - - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Activepieces Team - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - See less + +Hasznรกlja ezt a <a href="https://www.activepieces.com/docs/developers/building-pieces/create-action" target="_blank" rel="noopener">szemรฉlyre szabott elem telepรญtรฉsรฉhez</a>, amelyet รถn (vagy valaki mรกs) kรฉszรญtett. +Miutรกn az elem telepรญtรฉsre kerรผlt, hasznรกlhatja azt az รกramlatformรกzรณban. +<br><br>**Figyelem:** +Gyล‘zล‘djรถn meg rรณla, hogy megbรญzik az รญrรณban, mivel az elem hozzรกfรฉrรฉst kap az รกramlรกsi adatokhoz, รฉs lehet, hogy nem kompatibilis az aktuรกlis Activepieces verziรณval. - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - See more - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Use Template - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Verified icons created by lakonicon - Flaticon - - + Learn about this flow packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html 3,5 - Learn about this flow + Tudjon meg tรถbbet errล‘l az รกramlรกsrรณl - + You used a template for this flow and you can read our guide on how to set it up. packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html 6,8 - You used a template for this flow and you can read our guide on how to set it up. + Ehhez a folyamathoz sablont hasznรกlt, รฉs elolvashatja รบtmutatรณnkat a beรกllรญtรกsรกrรณl. - + Go to guide packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html 12 - Go to guide + Tovรกbb az รบtmutatรณhoz - + Ignore packages/ui/feature-templates/src/lib/template-blog-notification/template-blog-notification.component.html 18,20 - Ignore + Figyelmen kรญvรผl hagyni - + Use Template packages/ui/feature-templates/src/lib/template-card/template-card.component.html 8,10 - Use Template + Sablon hasznรกlata - + Apps packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html 1,3 - Apps + Alkalmazรกsok - + Select packages/ui/feature-templates/src/lib/templates-dialog/template-apps-dropdown/template-apps-dropdown.component.html 6 - Select - - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - Featured - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Load more - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - All ideas + Kivรกlasztรกs - + Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 - - Browse Templates - - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - A new way to explore fresh ideas from our partners - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 + 3 - View featured ideas + Bรถngรฉsszen a sablonok kรถzรถtt - + Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 - Search templates + Sablon keresรฉs - + Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 - Start from scratch + Kezdjรผk a legelejรฉrล‘l - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Filters - + Szลฑrล‘k diff --git a/packages/ui/core/src/locale/messages.id.xlf b/packages/ui/core/src/locale/messages.id.xlf index bfe645f25..fd11af389 100644 --- a/packages/ui/core/src/locale/messages.id.xlf +++ b/packages/ui/core/src/locale/messages.id.xlf @@ -60,27 +60,11 @@ Url Blog - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - Url gambar - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Deskripsi Unggulan - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 Filter @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + Konfirmasi @@ -458,22 +450,6 @@ Dibuat - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbot - Create @@ -486,7 +462,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -534,7 +510,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 Proyek @@ -542,7 +518,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 Penampilan @@ -550,18 +526,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Bagian-bagian + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Templates + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 Pengaturan + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Users + Version @@ -570,13 +562,29 @@ Versi - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - Tambahkan Bagian + Diperbarui + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Tambahkan Item @@ -718,7 +726,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -806,18 +814,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 Import Flow - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - Chatbot - Upgrade @@ -1176,11 +1176,31 @@ Email atau Kata Sandi tidak valid + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + You are not invited to any project, please contact your administrator. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Your user is inactive, please contact your administrator to activate it. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 Masuk @@ -1188,7 +1208,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Belum mempunyai akun? @@ -1196,7 +1216,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Daftar Sekarang @@ -1204,7 +1224,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 Lupa Kata Sandi? @@ -1272,14 +1292,6 @@ Kata Sandi tidak valid - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Hubungi admin Anda untuk mengirimi Anda undangan - Lowercase @@ -1546,7 +1558,7 @@ Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 Uji flow @@ -1554,7 +1566,7 @@ Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 Menyimpan... @@ -1568,18 +1580,6 @@ Lihat Saja - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Tambahkan Item - Value @@ -1918,50 +1918,6 @@ Trigger ini perlu mengirimkan data ke dalamnya, untuk digunakan sebagai data sampel - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Ganti Versi - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Draf - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - Diterbitkan - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - Diterbitkan - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Draf - Support @@ -1974,23 +1930,19 @@ Dukungan - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Home + Dashboard Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -2002,7 +1954,7 @@ Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2014,7 +1966,7 @@ Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 Import @@ -2022,7 +1974,7 @@ Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 Export @@ -2030,7 +1982,7 @@ Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2042,16 +1994,24 @@ Hapus + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 Tindakan ini akan menghapus flow, semua datanya, dan semua proses di latar belakang secara permanen. You can't undo this action. @@ -2110,15 +2070,27 @@ Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 Tambahkan 1 langkah lagi untuk menerbitkan + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + Diterbitkan + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 Flow Anda memiliki langkah-langkah yang tidak valid @@ -2126,7 +2098,7 @@ Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 Terbitkan Flow @@ -2134,7 +2106,7 @@ Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 Menyimpan @@ -2142,7 +2114,7 @@ Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 Menerbitkan @@ -2150,10 +2122,72 @@ Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 Terbitkan + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + Run Details @@ -2230,6 +2264,54 @@ Semua Iterasi + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Draf + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2460,7 +2542,7 @@ Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2808,6 +2890,26 @@ Silakan kirim data ke titik akhir webhook untuk menguji langkah tersebut. + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + Koneksi Baru + Name is required @@ -2840,34 +2942,6 @@ Nama diperlukan - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Hubungkan kembali - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - Koneksi Baru - Name can only contain letters, numbers and underscores @@ -2960,6 +3034,14 @@ wajib diisi + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Hubungkan kembali + You have to connect an account to continue @@ -3070,6 +3152,14 @@ Harap pastikan kredensial koneksi ini benar. + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Home + Ask the Community @@ -3098,11 +3188,11 @@ Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 Flow @@ -3110,11 +3200,11 @@ Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3134,11 +3224,11 @@ Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3150,11 +3240,11 @@ Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 Tim @@ -3170,27 +3260,15 @@ Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 Rencana - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Pengaturan Chatbot - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 Platform @@ -3206,14 +3284,6 @@ Aplikasi - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - Diperbarui - Delete Connection @@ -3226,7 +3296,7 @@ No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 Belum ada koneksi yang dibuat @@ -3278,7 +3348,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 Tanpa judul @@ -3354,11 +3424,11 @@ Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 Flow real time @@ -3369,7 +3439,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 Runs @@ -3380,7 +3450,7 @@ Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 Silakan hubungi tim dukungan karena flow yang Anda terbitkan bermasalah @@ -3574,11 +3644,23 @@ Selesai + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 Belum ada proses yang dijalankan @@ -3590,6 +3672,14 @@ Di sinilah Anda dapat memasang bagian yang dibuat sendiri atau kontributor. + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Tambahkan Bagian + No pieces installed @@ -3656,46 +3746,6 @@ Pastikan Anda memercayai penulisnya karena bagian tersebut akan memiliki akses ke data flow Anda dan mungkin tidak kompatibel dengan versi saat ini. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Tim AhaMake - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - Lihat lebih sedikit - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - Lihat lebih banyak - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Gunakan Templat - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Ikon terverifikasi yang dibuat oleh lakonicon - Flaticon - Learn about this flow @@ -3754,59 +3804,19 @@ Pilih - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - Unggulan - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Muat lebih banyak - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - Semua ide - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 Telusuri Templat - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - Sebuah cara baru untuk mengeksplorasi ide-ide baru dari mitra kami - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - Lihat ide unggulan - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 Cari templat @@ -3814,19 +3824,17 @@ Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 Mulai dari awal - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Filter - + Filters diff --git a/packages/ui/core/src/locale/messages.it.xlf b/packages/ui/core/src/locale/messages.it.xlf index 47f94ce30..2dd76e0a7 100644 --- a/packages/ui/core/src/locale/messages.it.xlf +++ b/packages/ui/core/src/locale/messages.it.xlf @@ -60,27 +60,11 @@ Url Del Blog - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - URL immagine - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Descrizione delle caratteristiche - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 Filtri @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + Conferma @@ -458,22 +450,6 @@ Creato - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbots - Create @@ -486,7 +462,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -534,7 +510,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 Progetti @@ -542,7 +518,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 Aspetto @@ -550,18 +526,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Pezzi + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Templates + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 Impostazioni + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Users + Version @@ -570,13 +562,29 @@ Versione - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - Aggiungi Pezzo + Aggiornato + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Aggiungi Elemento @@ -718,7 +726,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -806,18 +814,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 Importa flusso - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - Chatbot - Upgrade @@ -1176,11 +1176,31 @@ E-mail o password non valida + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + You are not invited to any project, please contact your administrator. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Your user is inactive, please contact your administrator to activate it. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 Accedi @@ -1188,7 +1208,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Non hai un account? @@ -1196,7 +1216,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Registrati ora @@ -1204,7 +1224,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 Hai dimenticato la password? @@ -1272,14 +1292,6 @@ Password non valida - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Contatta il tuo amministratore per inviarti un invito - Lowercase @@ -1543,7 +1555,7 @@ Completa passaggioTest flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 Test flow @@ -1551,7 +1563,7 @@ Completa passaggioSaving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 Saving... @@ -1565,18 +1577,6 @@ Completa passaggio Visualizza soltanto - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Aggiungi Elemento - Value @@ -1903,50 +1903,6 @@ risultato: Questo attivatore deve avere i dati caricati dal tuo account, per utilizzare come dati di esempio - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Cambia Versione - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Bozza - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - Pubblicato - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - Pubblicato - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Bozza - Support @@ -1959,23 +1915,19 @@ risultato: Supporto - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Inizio + Dashboard Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -1987,7 +1939,7 @@ risultato: packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -1999,7 +1951,7 @@ risultato: packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 Importa @@ -2007,7 +1959,7 @@ risultato: packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 Esporta @@ -2015,7 +1967,7 @@ risultato: packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2027,16 +1979,24 @@ risultato: Elimina + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 Questo eliminerร  definitivamente il flusso, tutti i suoi dati e qualsiasi esecuzione in background. Non รจ possibile annullare questa azione. @@ -2095,15 +2055,27 @@ risultato: packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 Aggiungi un altro passo per pubblicare + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + Pubblicato + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 Il tuo flusso ha passi non validi @@ -2111,7 +2083,7 @@ risultato: packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 Pubblica Flusso @@ -2119,7 +2091,7 @@ risultato: packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 Salvataggio @@ -2127,7 +2099,7 @@ risultato: packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 Pubblicazione @@ -2135,10 +2107,72 @@ risultato: packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 Pubblica + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + Run Details @@ -2215,6 +2249,54 @@ risultato: Tutte Le Iterazioni + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Bozza + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2445,7 +2527,7 @@ risultato: packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2787,6 +2869,26 @@ Esecuzione fallita () Si prega di inviare i dati al webhook endpoint per testare il passaggio. + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + Nuova connessione + Name is required @@ -2819,34 +2921,6 @@ Esecuzione fallita () Il nome รจ obbligatorio - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Riconetti - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - Nuova connessione - Name can only contain letters, numbers and underscores @@ -2939,6 +3013,14 @@ Esecuzione fallita () รจ obbligatorio + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Riconetti + You have to connect an account to continue @@ -3049,6 +3131,14 @@ Esecuzione fallita () Assicurati che le credenziali di questa connessione siano corrette. + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Inizio + Ask the Community @@ -3077,11 +3167,11 @@ Esecuzione fallita () Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 Flussi @@ -3089,11 +3179,11 @@ Esecuzione fallita () Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3113,11 +3203,11 @@ Esecuzione fallita () Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3129,11 +3219,11 @@ Esecuzione fallita () Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 Squadra @@ -3149,27 +3239,15 @@ Esecuzione fallita () Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 Piani - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Impostazioni Chatbot - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 Piattaforma @@ -3185,14 +3263,6 @@ Esecuzione fallita () Applicazioni - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - Aggiornato - Delete Connection @@ -3205,7 +3275,7 @@ Esecuzione fallita () No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 Nessuna connessione ancora creata @@ -3257,7 +3327,7 @@ Esecuzione fallita () packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 Senza titolo @@ -3333,11 +3403,11 @@ Esecuzione fallita () Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 Flusso in tempo reale @@ -3348,7 +3418,7 @@ Esecuzione fallita () packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 Esegue @@ -3359,7 +3429,7 @@ Esecuzione fallita () Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 Si prega di contattare il supporto perchรฉ il flusso pubblicato ha un problema @@ -3553,11 +3623,23 @@ Esecuzione fallita () Finito + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 Ancora nessuna esecuzione istantanea @@ -3569,6 +3651,14 @@ Esecuzione fallita () Questo รจ dove รจ possibile installare pezzi fatti da te o contributori. + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Aggiungi Pezzo + No pieces installed @@ -3635,46 +3725,6 @@ Esecuzione fallita () Assicurati di avere fiducia nell'autore in quanto il pezzo avrร  accesso ai tuoi dati di flusso e potrebbe non essere compatibile con l'attuale versione di Activepieces. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Team Activepieces - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - Mostra meno - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - Mostra altro - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Usa modello - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Icone verificate create da lakonicon - Flaticon - Learn about this flow @@ -3733,59 +3783,19 @@ Esecuzione fallita () Seleziona - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - In Evidenza - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Carica altro - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - Tutte le idee - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 Sfoglia i modelli - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - Un nuovo modo per esplorare nuove idee dai nostri partner - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - Visualizza idee in evidenza - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 Cerca modelli @@ -3793,19 +3803,17 @@ Esecuzione fallita () Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 Iniziare da zero - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Filtri - + Filters diff --git a/packages/ui/core/src/locale/messages.ja.xlf b/packages/ui/core/src/locale/messages.ja.xlf index d8b378c69..7ef7705f0 100644 --- a/packages/ui/core/src/locale/messages.ja.xlf +++ b/packages/ui/core/src/locale/messages.ja.xlf @@ -60,27 +60,11 @@ ใƒ–ใƒญใ‚ฐURL - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - ็”ปๅƒURL - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - ๆฉŸ่ƒฝใฎ่ชฌๆ˜Ž: - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 ็ตžใ‚Š่พผใฟ @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + ็ขบ่ช @@ -457,22 +449,6 @@ ไฝœๆˆๆธˆใฟ - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - ใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆ - Create @@ -485,7 +461,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -533,7 +509,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 ใƒ—ใƒญใ‚ธใ‚งใ‚ฏใƒˆ @@ -541,7 +517,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 ่กจ็คบ่จญๅฎš @@ -549,18 +525,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Pieces + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Templates + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 ่จญๅฎš + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Users + Version @@ -569,13 +561,29 @@ ใƒใƒผใ‚ธใƒงใƒณ - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - ใƒ”ใƒผใ‚นใ‚’่ฟฝๅŠ  + ๆ›ดๆ–ฐ + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + ใ‚ขใ‚คใƒ†ใƒ ใ‚’่ฟฝๅŠ  @@ -717,7 +725,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -805,18 +813,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 ใƒ•ใƒญใƒผใ‚’ใ‚คใƒณใƒใƒผใƒˆ - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - ใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆ - Upgrade @@ -1175,11 +1175,31 @@ ใƒกใ‚คใƒซใพใŸใฏใƒ‘ใ‚นใƒฏใƒผใƒ‰ใŒๆญฃใ—ใใŒใ‚ใ‚Šใพใ›ใ‚“ + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + You are not invited to any project, please contact your administrator. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Your user is inactive, please contact your administrator to activate it. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 ใ‚ตใ‚คใƒณใ‚คใƒณ @@ -1187,7 +1207,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 ใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ‚’ใŠๆŒใกใงใฏใ‚ใ‚Šใพใ›ใ‚“ใ‹๏ผŸ @@ -1195,7 +1215,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 ไปŠใ™ใ็™ป้Œฒ @@ -1203,7 +1223,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 ใƒ‘ใ‚นใƒฏใƒผใƒ‰ใ‚’ใŠๅฟ˜ใ‚Œใงใ™ใ‹? @@ -1271,14 +1291,6 @@ ใƒ‘ใ‚นใƒฏใƒผใƒ‰ใŒ็„กๅŠนใงใ™ - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - ๆ‹›ๅพ…็Šถใ‚’้€ไฟกใ™ใ‚‹ใซใฏ็ฎก็†่€…ใซ้€ฃ็ตกใ—ใฆใใ ใ•ใ„ - Lowercase @@ -1545,7 +1557,7 @@ Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 ใƒ•ใƒญใƒผใ‚’ใƒ†ใ‚นใƒˆ @@ -1553,7 +1565,7 @@ Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 ไฟๅญ˜ไธญ... @@ -1567,18 +1579,6 @@ ้–ฒ่ฆงใฎใฟ - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + ใ‚ขใ‚คใƒ†ใƒ ใ‚’่ฟฝๅŠ  - Value @@ -1917,50 +1917,6 @@ ใ‚ตใƒณใƒ—ใƒซใƒ‡ใƒผใ‚ฟใจใ—ใฆไฝฟ็”จใ™ใ‚‹ใซใฏใ€ใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ‹ใ‚‰ใƒ‡ใƒผใ‚ฟใ‚’่ชญใฟ่พผใ‚€ๅฟ…่ฆใŒใ‚ใ‚Šใพใ™ - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - ใƒใƒผใ‚ธใƒงใƒณใ‚’ๅˆ‡ใ‚Šๆ›ฟใˆใ‚‹ - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - ไธ‹ๆ›ธใ - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - ๅ…ฌ้–‹ๆธˆใฟ - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - ๅ…ฌ้–‹ๆธˆใฟ - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - ไธ‹ๆ›ธใ - Support @@ -1973,23 +1929,19 @@ ใ‚ตใƒใƒผใƒˆ - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - ใƒ›ใƒผใƒ  + Dashboard Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -2001,7 +1953,7 @@ Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2013,7 +1965,7 @@ Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 ใ‚คใƒณใƒใƒผใƒˆ @@ -2021,7 +1973,7 @@ Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 ใ‚จใ‚ฏใ‚นใƒใƒผใƒˆ @@ -2029,7 +1981,7 @@ Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2041,16 +1993,24 @@ ๅ‰Š้™ค + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 ใƒ•ใƒญใƒผใ€ใ™ในใฆใฎใƒ‡ใƒผใ‚ฟใ€ใŠใ‚ˆใณใƒใƒƒใ‚ฏใ‚ฐใƒฉใ‚ฆใƒณใƒ‰ๅฎŸ่กŒใ‚’ๅฎŒๅ…จใซๅ‰Š้™คใ—ใพใ™ใ€‚ ใ“ใฎๆ“ไฝœใฏๅ…ƒใซๆˆปใ›ใพใ›ใ‚“ใ€‚ @@ -2109,15 +2069,27 @@ Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 ๅ…ฌ้–‹ใ™ใ‚‹ใซใฏใ‚ใจ1ใ‚นใƒ†ใƒƒใƒ—ใ‚’่ฟฝๅŠ ใ—ใฆใใ ใ•ใ„ + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + ๅ…ฌ้–‹ๆธˆใฟ + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 ใƒ•ใƒญใƒผใซ็„กๅŠนใชใ‚นใƒ†ใƒƒใƒ—ใŒใ‚ใ‚Šใพใ™ @@ -2125,7 +2097,7 @@ Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 ใƒ•ใƒญใƒผใ‚’ๅ…ฌ้–‹ใ™ใ‚‹ @@ -2133,7 +2105,7 @@ Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 ไฟๅญ˜ไธญ @@ -2141,7 +2113,7 @@ Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 ๅ…ฌ้–‹ไธญ @@ -2149,10 +2121,72 @@ Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 ๅ…ฌ้–‹ + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + Run Details @@ -2229,6 +2263,54 @@ ๅ…จใฆใฎใ‚คใƒ†ใƒฌใƒผใ‚ทใƒงใƒณ + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + ไธ‹ๆ›ธใ + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2461,7 +2543,7 @@ Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2809,6 +2891,26 @@ Webhookใ‚จใƒณใƒ‰ใƒใ‚คใƒณใƒˆใซใƒ‡ใƒผใ‚ฟใ‚’้€ไฟกใ—ใฆใใ ใ•ใ„ใ€‚ + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + ๆ–ฐใ—ใ„ใ‚ณใƒใ‚ฏใ‚ทใƒงใƒณ + Name is required @@ -2841,34 +2943,6 @@ ๅๅ‰ใฏๅฟ…้ ˆใงใ™ - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - ๅ†ๆŽฅ็ถš - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - ๆ–ฐใ—ใ„ใ‚ณใƒใ‚ฏใ‚ทใƒงใƒณ - Name can only contain letters, numbers and underscores @@ -2961,6 +3035,14 @@ ใฏๅฟ…้ ˆใงใ™ + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + ๅ†ๆŽฅ็ถš + You have to connect an account to continue @@ -3071,6 +3153,14 @@ ๆŽฅ็ถšใฎ่ช่จผๆƒ…ๅ ฑใŒๆญฃใ—ใ„ใ“ใจใ‚’็ขบ่ชใ—ใฆใใ ใ•ใ„ใ€‚ + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + ใƒ›ใƒผใƒ  + Ask the Community @@ -3099,11 +3189,11 @@ Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 ใƒ•ใƒญใƒผ @@ -3111,11 +3201,11 @@ Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3135,11 +3225,11 @@ Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3151,11 +3241,11 @@ Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 ใƒใƒผใƒ  @@ -3171,27 +3261,15 @@ Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 ใƒ—ใƒฉใƒณ - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - ใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆ่จญๅฎš - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 ใƒ—ใƒฉใƒƒใƒˆใƒ•ใ‚ฉใƒผใƒ  @@ -3207,14 +3285,6 @@ ใ‚ขใƒ—ใƒช - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - ๆ›ดๆ–ฐ - Delete Connection @@ -3227,7 +3297,7 @@ No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 ใ‚ณใƒใ‚ฏใ‚ทใƒงใƒณใฏใพใ ไฝœๆˆใ•ใ‚Œใฆใ„ใพใ›ใ‚“ @@ -3279,7 +3349,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 ็„ก้กŒ @@ -3355,11 +3425,11 @@ Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 ใƒชใ‚ขใƒซใ‚ฟใ‚คใƒ  ใƒ•ใƒญใƒผ @@ -3370,7 +3440,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 ๅฎŸ่กŒ @@ -3381,7 +3451,7 @@ Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 ๅ…ฌ้–‹ใƒ•ใƒญใƒผใซๅ•้กŒใŒใ‚ใ‚‹ใŸใ‚ใ€ใ‚ตใƒใƒผใƒˆใซ้€ฃ็ตกใ—ใฆใใ ใ•ใ„ใ€‚ @@ -3575,11 +3645,23 @@ ็ต‚ไบ†ใ—ใพใ—ใŸ + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 ๅฎŸ่กŒใฏใพใ ้–‹ๅง‹ใ•ใ‚Œใฆใ„ใพใ›ใ‚“ @@ -3591,6 +3673,14 @@ ่‡ช่บซใ‚„ใ‚ณใƒณใƒˆใƒชใƒ“ใƒฅใƒผใ‚ฟใƒผใŒไฝœๆˆใ—ใŸใƒ”ใƒผใ‚นใ‚’ใ‚คใƒณใ‚นใƒˆใƒผใƒซใงใใพใ™ + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + ใƒ”ใƒผใ‚นใ‚’่ฟฝๅŠ  + No pieces installed @@ -3657,46 +3747,6 @@ Make sure you trust the author as the piece will have access to your flow data and it might not be compatible with the current version of Activepieces. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Activepieces ใƒใƒผใƒ  - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - ่กจ็คบใ‚’ๆธ›ใ‚‰ใ™ - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - ่ฉณ็ดฐใ‚’่กจ็คบ - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - ใ“ใฎใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‚’ไฝฟใ† - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Lakonicon ใŒไฝœๆˆใ—ใŸ็ขบ่ชๆธˆใฟใ‚ขใ‚คใ‚ณใƒณ - Flaticon - Learn about this flow @@ -3755,59 +3805,19 @@ ้ธๆŠž - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - ๆณจ็›ฎ - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - ๆ›ดใซ่ชญใฟ่พผใ‚€ - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - ใ™ในใฆใฎใ‚ขใ‚คใƒ‡ใ‚ข - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‚’่ฆ‹ใ‚‹ - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - ใƒ‘ใƒผใƒˆใƒŠใƒผใ‹ใ‚‰ใฎๆ–ฐใ—ใ„ใ‚ขใ‚คใƒ‡ใ‚ขใ‚’ๆŽขๆฑ‚ใ™ใ‚‹ - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - ๆณจ็›ฎใฎใ‚ขใ‚คใƒ‡ใ‚ขใ‚’่ฆ‹ใ‚‹ - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‚’ๆคœ็ดขใ™ใ‚‹ @@ -3815,19 +3825,17 @@ Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 ๆ–ฐ่ฆไฝœๆˆ - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - ็ตžใ‚Š่พผใฟ - + Filters diff --git a/packages/ui/core/src/locale/messages.nl.xlf b/packages/ui/core/src/locale/messages.nl.xlf index 9fc2d268f..b7f3e692a 100644 --- a/packages/ui/core/src/locale/messages.nl.xlf +++ b/packages/ui/core/src/locale/messages.nl.xlf @@ -60,27 +60,11 @@ Blog Url - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - Afbeelding url - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Uitgelichte beschrijving - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 Filters @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + Bevestigen @@ -458,22 +450,6 @@ Aangemaakt - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbots - Create @@ -486,7 +462,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -534,7 +510,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 Projecten @@ -542,7 +518,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 Uitstraling @@ -550,18 +526,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Pieces + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Templates + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 Instellingen + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Gebruikers + Version @@ -570,13 +562,29 @@ Versie - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - Piece toevoegen + Gewijzigd + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Voeg item toe @@ -718,7 +726,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -806,18 +814,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 Flow importeren - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - Chatbot - Upgrade @@ -1176,11 +1176,31 @@ Geen email ontvangen? Resend E-mailadres of wachtwoord onjuist + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + U bent niet uitgenodigd voor een project, neem contact op met uw administrator. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Uw account is inactief, neem contact op met uw beheerder om deze te activeren. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 Aanmelden @@ -1188,7 +1208,7 @@ Geen email ontvangen? Resend Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Heeft u geen account? @@ -1196,7 +1216,7 @@ Geen email ontvangen? Resend Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Meld je nu aan @@ -1204,7 +1224,7 @@ Geen email ontvangen? Resend Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 Wachtwoord vergeten? @@ -1272,14 +1292,6 @@ Geen email ontvangen? Resend Wachtwoord is ongeldig - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Neem contact op met uw beheerder om u een uitnodiging te sturen - Lowercase @@ -1424,21 +1436,21 @@ Geen email ontvangen? Resend Wachtwoord vergeten - + True packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts 71 - Waar + Waar - + False packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/branch-drawer.ts 71 - Onwaar + Onwaar Delete Step @@ -1464,13 +1476,13 @@ Geen email ontvangen? Resend Trigger aanpassen - + Can't move here packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/add-button-core.component.ts 21 - Kan hier niet verplaatsen + Kan hier niet verplaatsen Incomplete settings @@ -1504,14 +1516,14 @@ Geen email ontvangen? Resend Webhook trigger - + End packages/ui/feature-builder-canvas/src/lib/components/widgets/end-of-flow-widget/end-of-flow-widget.component.html 2,4 - Einde + Einde @@ -1530,7 +1542,7 @@ Geen email ontvangen? Resend - + @@ -1538,25 +1550,25 @@ Geen email ontvangen? Resend packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.html 11,13 - + - + Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 - Test flow + Test flow - + Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 - Bewaren... + Bewaren... View Only @@ -1568,18 +1580,6 @@ Geen email ontvangen? Resend Alleen weergeven - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Voeg item toe - Value @@ -1744,45 +1744,45 @@ Geen email ontvangen? Resend Test code - + Minimize Editor packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 13 - Minimize Editor + Editor minimaliseren - + Output packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 74,76 - Output + Uitgang - + Console packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 81,83 - Console + Console - + Copied to clipboard packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts 198 - Copied to clipboard + Gekopieerd naar het klembord - + Test Step First packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 42,44 - Test Step First + Test stap eerst This step needs to be tested in order to view its data. @@ -1918,50 +1918,6 @@ Geen email ontvangen? Resend Deze trigger moet gegevens ontvangen om als voorbeeldgegevens te gebruiken - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Versie wisselen - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Concept - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - Gepubliceerd - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - Gepubliceerd - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Concept - Support @@ -1974,23 +1930,19 @@ Geen email ontvangen? Resend Support - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Home + Dashboard Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -2002,7 +1954,7 @@ Geen email ontvangen? Resend Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2014,7 +1966,7 @@ Geen email ontvangen? Resend Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 Importeren @@ -2022,7 +1974,7 @@ Geen email ontvangen? Resend Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 Exporteren @@ -2030,7 +1982,7 @@ Geen email ontvangen? Resend Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2042,16 +1994,24 @@ Geen email ontvangen? Resend Verwijderen + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Ga naar map + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 Dit zal de flow permanent verwijderen, samen met al zijn gegevens en achtergrondprocessen. Deze actie kan niet worden teruggedraaid. @@ -2109,15 +2069,27 @@ Geen email ontvangen? Resend Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 Voeg nog 1 stap toe om te publiceren + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + Gepubliceerd + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 Uw flow heeft ongeldige stappen @@ -2125,7 +2097,7 @@ Geen email ontvangen? Resend Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 Publiceer Flow @@ -2133,7 +2105,7 @@ Geen email ontvangen? Resend Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 Bewaren @@ -2141,7 +2113,7 @@ Geen email ontvangen? Resend Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 Publiceren @@ -2149,10 +2121,72 @@ Geen email ontvangen? Resend Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 Publiceer + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versie geschiedenis + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versies + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Let op + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Jouw huidige concept versie zal worden overschreven door versie #. + + + + + Run Details @@ -2229,6 +2263,54 @@ Geen email ontvangen? Resend Alle iteraties + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Bekijken + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Concept + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + Bekijk + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Gebruik als concept + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2460,7 +2542,7 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2808,6 +2890,26 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Stuur gegevens naar het webhook-eindpunt om de stap te testen. + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + Nieuwe Connectie + Name is required @@ -2840,34 +2942,6 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Naam is vereist - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Opnieuw verbinden - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - Nieuwe Connectie - Name can only contain letters, numbers and underscores @@ -2960,6 +3034,14 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> is vereist + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Opnieuw verbinden + You have to connect an account to continue @@ -3070,6 +3152,14 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Controleer of de inloggegevens van deze verbindingen correct zijn. + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Home + Ask the Community @@ -3098,11 +3188,11 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 Flows @@ -3110,11 +3200,11 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3134,11 +3224,11 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3150,11 +3240,11 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 Team @@ -3170,27 +3260,15 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 Pakketten - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Chatbot instellingen - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 Platform @@ -3206,14 +3284,6 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> App - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - Gewijzigd - Delete Connection @@ -3226,7 +3296,7 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 Nog geen verbindingen gemaakt @@ -3278,7 +3348,7 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 Naamloos @@ -3354,11 +3424,11 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 Real time flow @@ -3369,7 +3439,7 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 Runs @@ -3380,7 +3450,7 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 Neem contact op met support omdat je gepubliceerde flow een probleem heeft @@ -3574,11 +3644,23 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Voltooid + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 Nog geen uitvoeringen geรฏnstantieerd @@ -3590,6 +3672,14 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Hier kun je door jezelf of bijdragers gemaakte pieces installeren. + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Piece toevoegen + No pieces installed @@ -3656,46 +3746,6 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Zorg ervoor dat je erop vertrouwt dat de auteur toegang heeft tot je flowdata en dat het mogelijk niet compatibel is met de huidige versie van Activepieces. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Activepieces Team - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - Minder weergeven - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - Meer weergeven - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Template Gebruiken - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Geverifieerde iconen gemaakt door lakonicon - Flaticon - Learn about this flow @@ -3754,59 +3804,19 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Selecteren - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - Uitgelicht - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Meer laden - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - Alle ideeรซn - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 Blader door de templates - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - Een nieuwe manier om nieuwe ideeรซn van onze partners te verkennen - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - Bekijk aanbevolen ideeรซn - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 Zoek een template @@ -3814,19 +3824,17 @@ retourrespons toevoegen aan het einde van je flow.<br> <br> Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 Start opnieuw - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Filters - + Filters diff --git a/packages/ui/core/src/locale/messages.pt.xlf b/packages/ui/core/src/locale/messages.pt.xlf index b3d78019b..3796d7df5 100644 --- a/packages/ui/core/src/locale/messages.pt.xlf +++ b/packages/ui/core/src/locale/messages.pt.xlf @@ -60,27 +60,11 @@ Url do blog - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - URL da imagem - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Descriรงรฃo do recurso - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 Filtros @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + Confirmar @@ -457,22 +449,6 @@ Criado - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbots - Create @@ -485,7 +461,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -533,7 +509,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 Projetos @@ -541,7 +517,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 Aparรชncia @@ -549,18 +525,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Peรงas + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Templates + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 Configuraรงรตes + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Usuรกrios + Version @@ -569,13 +561,29 @@ Versรฃo - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - Adicionar Pedaรงos + Atualizado + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Adicionar Item @@ -715,7 +723,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -803,18 +811,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 Importar Fluxo - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - Chatbot - Upgrade @@ -1172,11 +1172,31 @@ Senha ou e-mail invรกlido + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + Vocรช nรฃo foi convidado para nenhum projeto, entre em contato com seu administrador. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Sua conta estรก inativa, por favor entre em contato com o administrador para ativรก-lo. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 Iniciar sessรฃo @@ -1184,7 +1204,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Nรฃo tem conta? @@ -1192,7 +1212,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Cadastre-se agora @@ -1200,7 +1220,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 Esqueceu a senha? @@ -1268,14 +1288,6 @@ A senha รฉ invรกlida - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Entre em contato com o seu administrador para te enviar um convite - Lowercase @@ -1436,81 +1448,81 @@ Falso - + Delete Step packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/delete-flow-item-action/delete-flow-item-action.component.html 1 - Delete Step + Excluir etapa - + Duplicate Step packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/duplicate-step-action/duplicate-step-action.component.html 1 - Duplicate Step + Duplicar Etapa - + Change Trigger packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/replace-trigger-action/replace-trigger-action.component.html 1 - Change Trigger + Alterar disparador - + Can't move here packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/add-button-core.component.ts 21 - Can't move here + Nรฃo รฉ possรญvel mover nesta รกrea - + Incomplete settings packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.html 35 - Incomplete settings + Configuraรงรตes incompletas - + Loop packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts 197 - Loop + Repetir - + Choose a trigger packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts 207 - Choose a trigger + Escolha um disparador - + Webhook trigger packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-content/flow-item-content.component.ts 209 - Webhook trigger + Disparador Webhook - + End packages/ui/feature-builder-canvas/src/lib/components/widgets/end-of-flow-widget/end-of-flow-widget.component.html 2,4 - End + Finalizar - + Complete step @@ -1520,13 +1532,12 @@ packages/ui/feature-builder-canvas/src/lib/components/widgets/incomplete-steps-widget/incomplete-steps-widget.component.html 5,7 - - Complete step - - - + +Complete a etapa + + - + @@ -1534,49 +1545,36 @@ packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.html 11,13 - - - + + - + Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 - Test flow + Testar fluxo - + Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 - Saving... + Salvando... - + View Only packages/ui/feature-builder-canvas/src/lib/components/widgets/view-only-mode-widget/view-only-mode-widget.component.html 2,4 - View Only + Somente visualizar - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Add Item - - + Value @@ -1585,92 +1583,91 @@ packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 5 - - Value - - + +Valor + - + First value is required packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 9,11 - First value is required + O primeiro valor รฉ obrigatรณrio - + Condition packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 32 - Condition + Condiรงรฃo - + Condition is required packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 38,40 - Condition is required + Condiรงรฃo requerida - + Second Value packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 47 - Second Value + Segundo valor - + Second value is required packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 51,53 - Second value is required + O segundo valor รฉ obrigatรณrio - + Case sensitive packages/ui/feature-builder-form-controls/src/lib/branch-condition-form-control/branch-condition-form-control.component.html 64,66 - Case sensitive + Diferenciar Maiรบsculas e Minรบsculas - + And packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html 11,13 - And + E - + + And packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html 21,23 - + And + + E - + + Or packages/ui/feature-builder-form-controls/src/lib/branch-conditions-group-form-control/branch-conditions-group-form-control.component.html 25,27 - + Or + + Ou - + Add NPM Package packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 1 - Add NPM Package + Adicionar novo pacote NPM - + Please type the name of an NPM package. @@ -1679,124 +1676,124 @@ packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 3,6 - - Please type the name of an NPM package. + +Por favor, informe o nome do pacote NPM - + Package name is required packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 11,13 - Package name is required + ร‰ necessรกrio o nome do pacote - + Package not found packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 15,17 - Package not found + Pacote nรฃo encontrado - + The latest version will be fetched and added to package.json packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 21,23 - The latest version will be fetched and added to package.json + A versรฃo mais recente serรก baixada e adicionada ao package.json - + Add packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/add-npm-package-modal/add-npm-package-modal.component.html 31,33 - Add + Adicionar - + Code Editor packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 3 - Code Editor + Editor de cรณdigo - + Add npm package packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 6,8 - Add npm package + Adicionar pacote npm - + Test code packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 10,11 - Test code + Cรณdigo de teste - + Minimize Editor packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 13 - Minimize Editor + Minimizar Editor - + Output packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 74,76 - Output + Resultado - + Console packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 81,83 - Console + Console - + Copied to clipboard packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts 198 - Copied to clipboard + Copiado para a รกrea de transferรชncia - + Test Step First packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 42,44 - Test Step First + Teste do Primeiro Passo - + This step needs to be tested in order to view its data. packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 45,47 - This step needs to be tested in order to view its data. + Esta etapa precisa ser testada em ordem para ver seus dados. - + Go to step packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 50,52 - Go to step + Vรก para a etapa - + Describe the path to the property using JSON format @@ -1807,22 +1804,22 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 4,6 - - Describe the path to the property using JSON format + + Descreva o caminho para a propriedade usando o formato JSON - + Path is invalid packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 12,14 - Path is invalid + Caminho invรกlido - + Path must begin with @@ -1831,20 +1828,20 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 16,18 - - Path must begin with + + O caminho deve comeรงar com - + Select packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/generic-mention-item/generic-mention-item.component.html 4,5 - Select + Selecionar - + result: @@ -1855,14 +1852,14 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html 37,40 - - result: + + resultado: - + Insert packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html @@ -1876,17 +1873,17 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/step-mentions-tree/step-mentions-tree.component.html 44,45 - Insert + Inserir - + This trigger needs to have data loaded from your account, to use as sample data packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html 56,58 - This trigger needs to have data loaded from your account, to use as sample data + Esta aรงรฃo precisa ser dos dados carregados de sua conta, para ser usado como dados de exemplo - + Go to trigger packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/piece-trigger-mention-item/piece-trigger-mention-item.component.html @@ -1896,69 +1893,25 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html 33,34 - Go to trigger + Vรก para a aรงรฃo - + Send sample data first packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html 25,27 - Send sample data first + Envie dados de amostra primeiro - + This trigger needs to have data sent to it, to use as sample data packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/webhook-trigger-mention-item/webhook-trigger-mention-item.component.html 28,30 - This trigger needs to have data sent to it, to use as sample data - - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Switch Version - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Draft - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - Published - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - Published + Esta aรงรฃo precisa de dados, para serem usados como amostra - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Draft - - + Support packages/ui/feature-builder-header/src/lib/feedback/support.component.html @@ -1968,65 +1921,61 @@ packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html 37 - Support + Suporte - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Home + Dashboard - + Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html 57 - Rename + Renomear - + Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html 77 - Duplicate + Duplicar - + Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 - Import + Importar - + Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 - Export + Exportar - + Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2036,33 +1985,41 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html 64 - Delete + Remover - + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Ir para a pasta + + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 - This will permanently delete the flow, all its data and any background runs. - You can't undo this action. + Isso vai permanentemente deletar o fluxo, todos os dados e qualquer background executado. + Vocรช nรฃo pode desfazer esta aรงรฃo. - + Import Flow packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 1,3 - Import Flow + Importar Fluxo - + Important: Importing a flow will overwrite your current flow. @@ -2072,83 +2029,157 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 9 - - Important: Importing a flow will overwrite your current flow. + + Importante: a importaรงรฃo de um fluxo sobrescreverรก seu fluxo atual. - + File packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 12 - File + Arquivo - + Import packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 26,28 - Import + Importar - + Edit Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.html 16,18 - Edit Flow + Editar fluxo - + Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 + + Adicionar mais 1 etapa para publicar + + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 - Add 1 more step to publish + Publicado - + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 - Your flow has invalid steps + Seu fluxo tem etapas invรกlidas - + Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 - Publish Flow + Publicar fluxo - + Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 - Saving + Salvando - + Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 - Publishing + Publicando - + Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 + + Publicar + + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Histรณrico de versรตes + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 - Publish + + Your current draft version will be overwritten with version #. + + + + Run Details @@ -2226,6 +2257,54 @@ All Iterations + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Draft + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2456,7 +2535,7 @@ Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2610,7 +2689,7 @@ Testing Failed - + Retest packages/ui/feature-builder-test-steps/src/lib/test-action/test-action.component.html @@ -2624,33 +2703,33 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 30 - Retest + Testar novamente - + Test this trigger to generate sample data that can be used in the next steps packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html 10 - Test this trigger to generate sample data that can be used in the next steps + Teste esta aรงรฃo para gerar dados de amostra que podem ser usados nas prรณximas etapas - + Test Trigger packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html 16,17 - Test Trigger + Testar aรงรฃo - + Use Mock Data packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html 22,23 - Use Mock Data + Usar dados de amostra - + Data packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html @@ -2664,9 +2743,9 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 34,36 - Data + Dados - + (Result ) @@ -2683,12 +2762,12 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 39 - - (Result ) + + (Resultado) - + Testing trigger packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html @@ -2698,9 +2777,9 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 54 - Testing trigger + Testando aรงรฃo - + Cancel packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html @@ -2710,9 +2789,9 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 57 - Cancel + Cancelar - + Action Required packages/ui/feature-builder-test-steps/src/lib/test-piece-webhook-trigger/test-piece-webhook-trigger.component.html @@ -2722,17 +2801,17 @@ packages/ui/feature-builder-test-steps/src/lib/test-webhook-trigger/test-webhook-trigger.component.html 63 - Action Required + Aรงรฃo necessรกria - + Load sample data from your account to be used in the next steps packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html 13 - Load sample data from your account to be used in the next steps + Carregue dados de exemplo de sua conta para ser usado nos prรณximos passos - + Load data packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html @@ -2746,7 +2825,7 @@ packages/ui/feature-builder-test-steps/src/lib/test-polling-trigger/test-polling-trigger.component.html 69 - Load data + Carregar dados This sample data can be used in the next steps @@ -2804,6 +2883,26 @@ Please send data to the webhook endpoint to test the step. + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + New Connection + Name is required @@ -2836,34 +2935,6 @@ Name is required - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Reconnect - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - New Connection - Name can only contain letters, numbers and underscores @@ -2956,6 +3027,14 @@ is required + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Reconnect + You have to connect an account to continue @@ -3066,6 +3145,14 @@ Please make sure this connections's credentials are correct. + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Home + Ask the Community @@ -3094,11 +3181,11 @@ Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 Flows @@ -3106,11 +3193,11 @@ Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3130,11 +3217,11 @@ Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3146,11 +3233,11 @@ Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 Team @@ -3166,27 +3253,15 @@ Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 Plans - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Chatbot settings - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 Platform @@ -3202,14 +3277,6 @@ App - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - Updated - Delete Connection @@ -3222,7 +3289,7 @@ No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 No connections created yet @@ -3274,7 +3341,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 Untitled @@ -3350,11 +3417,11 @@ Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 Real time flow @@ -3365,7 +3432,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 Runs @@ -3376,7 +3443,7 @@ Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 Please contact support as your published flow has a problem @@ -3570,11 +3637,23 @@ Finished + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 No runs instantiated yet @@ -3586,6 +3665,14 @@ This is where you can install pieces made by yourself or contributors. + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Adicionar Pedaรงos + No pieces installed @@ -3652,46 +3739,6 @@ Make sure you trust the author as the piece will have access to your flow data and it might not be compatible with the current version of Activepieces. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Activepieces Team - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - See less - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - See more - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Use Template - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Verified icons created by lakonicon - Flaticon - Learn about this flow @@ -3750,59 +3797,19 @@ Select - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - Featured - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Load more - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - All ideas - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 Browse Templates - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - A new way to explore fresh ideas from our partners - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - View featured ideas - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 Search templates @@ -3810,19 +3817,17 @@ Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 Start from scratch - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Filters - + Filters diff --git a/packages/ui/core/src/locale/messages.vi.xlf b/packages/ui/core/src/locale/messages.vi.xlf index e3c53a095..96d3ba466 100644 --- a/packages/ui/core/src/locale/messages.vi.xlf +++ b/packages/ui/core/src/locale/messages.vi.xlf @@ -60,27 +60,11 @@ Url blog - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - Url hรฌnh แบฃnh - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - Mรด tแบฃ - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 Bแป™ lแปc @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + Xรกc nhแบญn @@ -458,22 +450,6 @@ ฤรฃ tแบกo - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - Chatbots - Create @@ -486,7 +462,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -534,7 +510,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 Dแปฑ รกn @@ -542,7 +518,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 Biแปƒu mแบซu @@ -550,18 +526,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 Pieces + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + Mแบซu + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 Cร i ฤ‘แบทt + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Ngฦฐแปi dรนng + Version @@ -570,13 +562,29 @@ Phiรชn bแบฃn - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - Thรชm Piece + ฤรฃ cแบญp nhแบญt + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + Thรชm Item @@ -718,7 +726,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -806,18 +814,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 Nhแบญp Flow - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - Chatbot - Upgrade @@ -1176,11 +1176,31 @@ Email hoแบทc mแบญt khแบฉu khรดng hแปฃp lแป‡ + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + Bแบกn chฦฐa ฤ‘ฦฐแปฃc mแปi tham gia bแบฅt kแปณ dแปฑ รกn nร o, vui lรฒng liรชn hแป‡ vแป›i quแบฃn trแป‹ viรชn cแปงa bแบกn. + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Ngฦฐแปi dรนng cแปงa bแบกn khรดng hoแบกt ฤ‘แป™ng, vui lรฒng liรชn hแป‡ vแป›i quแบฃn trแป‹ viรชn ฤ‘แปƒ kรญch hoแบกt. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 ฤฤƒng nhแบญp @@ -1188,7 +1208,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 Bแบกn khรดng cรณ tร i khoแบฃn? @@ -1196,7 +1216,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 ฤฤƒng kรฝ ngay @@ -1204,7 +1224,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 Quรชn mแบญt khแบฉu? @@ -1272,14 +1292,6 @@ Mแบญt khแบฉu khรดng hแปฃp lแป‡ - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - Liรชn hแป‡ vแป›i quแบฃn trแป‹ viรชn ฤ‘แปƒ nhแบญn lแปi mแปi - Lowercase @@ -1546,7 +1558,7 @@ Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 Kiแปƒm tra flow @@ -1554,7 +1566,7 @@ Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 ฤang lฦฐu... @@ -1568,18 +1580,6 @@ Chแป‰ xem - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + Thรชm Item - Value @@ -1744,45 +1744,45 @@ Kiแปƒm tra Code - + Minimize Editor packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 13 - Minimize Editor + Thu nhแป trรฌnh chแป‰nh sแปญa - + Output packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 74,76 - Output + ฤแบงu ra - + Console packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 81,83 - Console + Bแบฃng ฤ‘iแปu khiแปƒn - + Copied to clipboard packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts 198 - Copied to clipboard + Sao chรฉp vร o clipboard - + Test Step First packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 42,44 - Test Step First + Cแบงn kiแปƒm tra trฦฐแป›c This step needs to be tested in order to view its data. @@ -1918,50 +1918,6 @@ Trigger nร y cแบงn cรณ dแปฏ liแป‡u ฤ‘ฦฐแปฃc gแปญi tแป›i nรณ, ฤ‘แปƒ sแปญ dแปฅng lร m dแปฏ liแป‡u mแบซu - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - Chuyแปƒn ฤ‘แป•i phiรชn bแบฃn - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - Bแบฃn nhรกp - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - ฤรฃ xuแบฅt bแบฃn - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - ฤรฃ xuแบฅt bแบฃn - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - Bแบฃn nhรกp - Support @@ -1974,23 +1930,19 @@ Hแป— trแปฃ - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - Trang chแปง + Bแบฃng ฤ‘iแปu khiแปƒn Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -2002,7 +1954,7 @@ Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2014,7 +1966,7 @@ Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 Nhแบญp @@ -2022,7 +1974,7 @@ Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 Xuแบฅt @@ -2030,7 +1982,7 @@ Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2042,16 +1994,24 @@ Xรณa + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + ฤi ฤ‘แบฟn Thฦฐ mแปฅc + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 Thao tรกc nร y sแบฝ xรณa vฤฉnh viแป…n flow, tแบฅt cแบฃ dแปฏ liแป‡u cแปงa flow vร  mแปi hoแบกt ฤ‘แป™ng chแบกy nแปn. Bแบกn khรดng thแปƒ hoร n tรกc hร nh ฤ‘แป™ng nร y. @@ -2109,15 +2069,27 @@ Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 Thรชm 1 step nแปฏa ฤ‘แปƒ xuแบฅt bแบฃn + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + ฤรฃ xuแบฅt bแบฃn + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 Flow cแปงa bแบกn cรณ cรกc step khรดng hแปฃp lแป‡ @@ -2125,7 +2097,7 @@ Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 Xuแบฅt bแบฃn Flow @@ -2133,7 +2105,7 @@ Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 ฤang lฦฐu @@ -2141,7 +2113,7 @@ Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 ฤang xuแบฅt bแบฃn @@ -2149,10 +2121,72 @@ Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 Xuแบฅt bแบฃn + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Lแป‹ch sแปญ phiรชn bแบฃn + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Phiรชn bแบฃn + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Cแบฃnh bรกo + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Phiรชn bแบฃn nhรกp hiแป‡n tแบกi cแปงa bแบกn sแบฝ bแป‹ ghi ฤ‘รจ bแบฑng phiรชn bแบฃn #. + + + + + Run Details @@ -2229,6 +2263,54 @@ Tแบฅt cแบฃ cรกc lแบงn lแบทp lแบกi + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + ฤang xem + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + Bแบฃn nhรกp + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + Xem + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Sแปญ dแปฅng lร m bแบฃn nhรกp + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2459,7 +2541,7 @@ Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2807,6 +2889,26 @@ Vui lรฒng gแปญi dแปฏ liแป‡u ฤ‘แบฟn Webhook ฤ‘แปƒ kiแปƒm tra bฦฐแป›c nร y. + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + Tแบกo kแบฟt nแป‘i + Name is required @@ -2839,34 +2941,6 @@ Tรชn lร  bแบฏt buแป™c - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - Kแบฟt nแป‘i lแบกi - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - Tแบกo kแบฟt nแป‘i - Name can only contain letters, numbers and underscores @@ -2959,6 +3033,14 @@ lร  bแบฏt buแป™c + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + Kแบฟt nแป‘i lแบกi + You have to connect an account to continue @@ -3069,6 +3151,14 @@ Hรฃy ฤ‘แบฃm bแบฃo thรดng tin ฤ‘ฤƒng nhแบญp cแปงa kแบฟt nแป‘i nร y lร  chรญnh xรกc. + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + Trang chแปง + Ask the Community @@ -3097,23 +3187,23 @@ Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 Luแป“ng - + Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3127,17 +3217,17 @@ packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html 9 - Runs + Runs Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3149,11 +3239,11 @@ Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 ฤแป™i ngลฉ @@ -3169,27 +3259,15 @@ Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 Kแบฟ hoแบกch - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Cร i ฤ‘แบทt Chatbot - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 Nแปn tแบฃng @@ -3205,14 +3283,6 @@ แปจng dแปฅng - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - ฤรฃ cแบญp nhแบญt - Delete Connection @@ -3225,7 +3295,7 @@ No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 Chฦฐa cรณ kแบฟt nแป‘i nร o ฤ‘ฦฐแปฃc tแบกo @@ -3277,7 +3347,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 Khรดng cรณ tiรชu ฤ‘แป @@ -3353,11 +3423,11 @@ Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 Flow thแปi gian thแปฑc @@ -3368,7 +3438,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 Runs @@ -3379,7 +3449,7 @@ Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 Vui lรฒng liรชn hแป‡ vแป›i bแป™ phแบญn hแป— trแปฃ vรฌ cรณ vแบฅn ฤ‘แป vแป›i flow ฤ‘รฃ xuแบฅt bแบฃn cแปงa bแบกn @@ -3573,11 +3643,23 @@ Hoร n thร nh + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 Chฦฐa cรณ flow nร o ฤ‘ฦฐแปฃc chแบกy @@ -3589,6 +3671,14 @@ ฤรขy lร  nฦกi bแบกn cรณ thแปƒ cร i ฤ‘แบทt cรกc piece do chรญnh bแบกn hoแบทc nhแปฏng ngฦฐแปi ฤ‘รณng gรณp tแบกo ra. + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + Thรชm Piece + No pieces installed @@ -3655,46 +3745,6 @@ Hรฃy ฤ‘แบฃm bแบฃo rแบฑng bแบกn tin tฦฐแปŸng tรกc giแบฃ vรฌ piece nร y sแบฝ cรณ quyแปn truy cแบญp vร o dแปฏ liแป‡u flow cแปงa bแบกn vร  nรณ cรณ thแปƒ khรดng tฦฐฦกng thรญch vแป›i phiรชn bแบฃn hiแป‡n tแบกi. - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Team - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - Rรบt gแปn - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - Xem thรชm - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - Sแปญ dแปฅng Template nร y - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - Cรกc biแปƒu tฦฐแปฃng ฤ‘รฃ ฤ‘ฦฐแปฃc xรกc minh ฤ‘ฦฐแปฃc tแบกo bแปŸi lakonicon - Flaticon - Learn about this flow @@ -3753,59 +3803,19 @@ Chแปn - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - Nรดฬ‰i bรขฬฃt - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - Tแบฃi thรชm - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - Tแบฅt cแบฃ รฝ tฦฐแปŸng - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 Duyแป‡t template - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - Mแป™t cรกch ฤ‘แปƒ khรกm phรก nhแปฏng รฝ tฦฐแปŸng mแป›i tแปซ cรกc ฤ‘แป‘i tรกc cแปงa chรบng tรดi - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - Xem cรกc รฝ tฦฐแปŸng nแป•i bแบญt - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 Tรฌm kiแบฟm template @@ -3813,19 +3823,17 @@ Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 Bแบฏt ฤ‘แบงu tแปซ mแบซu trแป‘ng - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - Bแป™ lแปc - + Bแป™ lแปc diff --git a/packages/ui/core/src/locale/messages.zh.xlf b/packages/ui/core/src/locale/messages.zh.xlf index 1710e05eb..57a70f086 100644 --- a/packages/ui/core/src/locale/messages.zh.xlf +++ b/packages/ui/core/src/locale/messages.zh.xlf @@ -60,27 +60,11 @@ ๅšๅฎข็ฝ‘ๅ€ - - Image url - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 28 - - ๅ›พ็‰‡้“พๆŽฅ - - - Featured Description - - packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 36 - - ๅŠŸ่ƒฝๆ่ฟฐ - Filters packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 40 + 27 ่ฟ‡ๆปคๅ™จ @@ -88,7 +72,7 @@ Cancel packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 55,57 + 42,44 packages/ee/project-members/src/lib/invite-project-member-dialog/invite-project-member.component.html @@ -114,6 +98,10 @@ packages/ui/feature-builder-header/src/lib/import-flow-dialogue/import-flow-dialogue.component.html 22,24 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 23,25 + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html 56,58 @@ -156,7 +144,7 @@ Confirm packages/ee/components/src/lib/share-flow-template-dialog/share-flow-template-dialog.component.html - 59,61 + 46,48 packages/ui/common/src/lib/components/delete-enity-dialog/delete-entity-dialog.component.html @@ -166,6 +154,10 @@ packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/custom-path-mention-dialog/custom-path-mention-dialog.component.html 29,31 + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 26,28 + ็กฎ่ฎค @@ -457,22 +449,6 @@ ๅทฒๅˆ›ๅปบ - - Chatbots - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 91 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 59 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 81 - - ่Šๅคฉๆœบๅ™จไบบ - Create @@ -485,7 +461,7 @@ Edit packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 79 + 83 packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html @@ -533,7 +509,7 @@ Projects packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 42 + 41 ้กน็›ฎ @@ -541,7 +517,7 @@ Appearance packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 48 + 47 ๅค–่ง‚ @@ -549,18 +525,34 @@ Pieces packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 55 + 53 ๅ— + + Templates + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 59 + + ๆจกๆฟ + Settings packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 61 + 71 ่ฎพ็ฝฎ + + Users + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts + 65 + + Users + Version @@ -569,13 +561,29 @@ ็‰ˆๆœฌ - - Add Piece + + Updated - packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html - 29,31 + packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html + 33 - ๆทปๅŠ ๅ— + ๅทฒๆ›ดๆ–ฐ + + + + Add Item + + packages/ui/common/src/lib/components/array-form-control/array-form-control.component.html + 23,24 + + + packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html + 30,31 + + + packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html + 37,38 + + + ๆทปๅŠ ้กน็›ฎ @@ -716,7 +724,7 @@ packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 103 + 53 packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html @@ -804,18 +812,10 @@ Import Flow packages/ui/core/src/app/app-routing.module.ts - 29 + 25 ๅฏผๅ…ฅๆต็จ‹ - - Chatbot - - packages/ui/core/src/app/app-routing.module.ts - 37 - - ่Šๅคฉๆœบๅ™จไบบ - Upgrade @@ -1169,11 +1169,31 @@ ๆ— ๆ•ˆ็š„้‚ฎ็ฎฑๆˆ–ๅฏ†็  + + You are not invited to any project, please contact your administrator. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 20,22 + + + packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html + 58,60 + + ๆ‚จๆฒกๆœ‰่ขซ้‚€่ฏทๅŠ ๅ…ฅไปปไฝ•้กน็›ฎ๏ผŒ่ฏท่”็ณปๆ‚จ็š„็ฎก็†ๅ‘˜ใ€‚ + + + Your user is inactive, please contact your administrator to activate it. + + packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html + 23,25 + + Your user is inactive, please contact your administrator to activate it. + Sign in packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 29,31 + 36,38 ็™ปๅฝ• @@ -1181,7 +1201,7 @@ Don't have an account? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 ่ฟ˜ๆฒกๆœ‰่ดฆๆˆท๏ผŸ @@ -1189,7 +1209,7 @@ Sign up now packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 36 + 43 ็ซ‹ๅณๆณจๅ†Œ @@ -1197,7 +1217,7 @@ Forgot password? packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html - 40 + 47 ๅฟ˜่ฎฐๅฏ†็ ๏ผŸ @@ -1265,14 +1285,6 @@ ๅฏ†็ ๆ— ๆ•ˆ - - Contact your admin to send you an invite - - packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html - 58,60 - - ่ฏท่”็ณปๆ‚จ็š„็ฎก็†ๅ‘˜ๅ‘้€้‚€่ฏท - Lowercase @@ -1539,7 +1551,7 @@ Test flow packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 53 + 52 ๆต‹่ฏ•ๆต็จ‹ @@ -1547,7 +1559,7 @@ Saving... packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts - 54 + 53 ไฟๅญ˜ไธญ... @@ -1561,18 +1573,6 @@ ไป…ๆŸฅ็œ‹ - - + Add Item - - packages/ui/feature-builder-form-controls/src/lib/array-form-control/array-form-control.component.html - 30,31 - - - packages/ui/feature-builder-form-controls/src/lib/dictionary-form-control/dictionary-form-control.component.html - 37,38 - - + ๆทปๅŠ ้กน็›ฎ - Value @@ -1737,45 +1737,45 @@ ๆต‹่ฏ•ไปฃ็  - + Minimize Editor packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 13 - Minimize Editor + ๆœ€ๅฐๅŒ–็ผ–่พ‘ๅ™จ - + Output packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 74,76 - Output + ่พ“ๅ‡บ - + Console packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.html 81,83 - Console + ๆŽงๅˆถๅฐ - + Copied to clipboard packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts 198 - Copied to clipboard + ๅทฒๅคๅˆถๅˆฐๅ‰ชๅˆ‡ๆฟ - + Test Step First packages/ui/feature-builder-form-controls/src/lib/interpolating-text-form-control/mentions-list/action-mention-item/action-mention-item.component.html 42,44 - Test Step First + ่ฏทๅ…ˆๆต‹่ฏ•ๆญฅ้ชค This step needs to be tested in order to view its data. @@ -1911,50 +1911,6 @@ ๆญค่งฆๅ‘ๅ™จ้œ€่ฆๅ…ˆๅฐ†ๆ•ฐๆฎๅ‘้€็ป™ๅฎƒ๏ผŒไปฅไฝœไธบ็คบไพ‹ๆ•ฐๆฎ - - Switch Version - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 18,20 - - ๅˆ‡ๆข็‰ˆๆœฌ - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 21,23 - - ่‰็จฟ - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html - 24,26 - - ๅทฒๅ‘ๅธƒ - - - Published - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 36 - - - packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 84 - - ๅทฒๅ‘ๅธƒ - - - Draft - - packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts - 38 - - ่‰็จฟ - Support @@ -1967,23 +1923,19 @@ ๆ”ฏๆŒ - - Home + + Dashboard packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html 14 - - packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html - 4 - - ้ฆ–้กต + Dashboard Rename packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 46 + 43 packages/ui/feature-dashboard/src/lib/pages/flows-table/folders-list/folders-list.component.html @@ -1995,7 +1947,7 @@ Duplicate packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 53 + 50 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2007,7 +1959,7 @@ Import packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 59 + 56 ๅฏผๅ…ฅ @@ -2015,7 +1967,7 @@ Export packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 66 + 63 ๅฏผๅ‡บ @@ -2023,7 +1975,7 @@ Delete packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html - 75 + 72 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.html @@ -2035,16 +1987,24 @@ ๅˆ ้™ค + + Go to folder + + packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts + 51 + + Go to folder + This will permanently delete the flow, all its data and any background runs. You can't undo this action. packages/ui/feature-builder-header/src/lib/flow-builder-header.component.ts - 131,132 + 141,142 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 121,122 + 119,120 ่ฟ™ๅฐ†ไผšๆฐธไน…ๅˆ ้™คๆญคๆต็จ‹๏ผŒๅŒ…ๆ‹ฌๅฎƒๆ‰€ๆœ‰ๆ•ฐๆฎๅ’ŒๅŽๅฐใ€‚ ๆ‚จไธ่ƒฝๆ’คๆถˆๆญคๆ“ไฝœใ€‚ @@ -2103,15 +2063,27 @@ Add 1 more step to publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 82 + 86 ๅ†ๆทปๅŠ ไธ€ไธชๆญฅ้ชคๅ‘ๅธƒ + + Published + + packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts + 88 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 29 + + ๅทฒๅ‘ๅธƒ + Your flow has invalid steps packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 86 + 90 ๆ‚จ็š„ๆต็จ‹ๅญ˜ๅœจๆ— ๆ•ˆ็š„ๆญฅ้ชค @@ -2119,7 +2091,7 @@ Publish Flow packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 88 + 92 ๅ‘ๅธƒๆต็จ‹ @@ -2127,7 +2099,7 @@ Saving packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 94 + 98 ไฟๅญ˜ไธญ @@ -2135,7 +2107,7 @@ Publishing packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 96 + 100 ๅ‘ๅธƒไธญ @@ -2143,10 +2115,72 @@ Publish packages/ui/feature-builder-header/src/lib/publish-button/publish-button.component.ts - 98 + 102 ๅ‘ๅธƒ + + Versions History + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 2 + + Versions History + + + Versions + + packages/ui/feature-builder-header/src/lib/version-history-button/version-history-button.component.html + 6 + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.ts + 35 + + Versions + + + + Warning + + + + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 2,7 + + + Warning + + + + + + + + + + + Your current draft version will be overwritten with version #. + + + + + + packages/ui/feature-builder-left-sidebar/src/lib/dialogs/use-as-draft-confirmation-dialog/use-as-draft-confirmation-dialog.component.html + 14,15 + + + Your current draft version will be overwritten with version #. + + + + + Run Details @@ -2223,6 +2257,54 @@ ๆ‰€ๆœ‰่ฟญไปฃ + + Viewing + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 18 + + Viewing + + + Draft + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 23 + + ่‰็จฟ + + + + View + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 40,42 + + + View + + + + + + + Use as draft + + + + + packages/ui/feature-builder-left-sidebar/src/lib/version-history/version-history.component.html + 47,49 + + + Use as draft + + + + If you are expecting a reply from this webhook, append <b>/sync</b> to the URL.<br> <br> @@ -2453,7 +2535,7 @@ Uncategorized packages/ui/feature-builder-store/src/lib/store/builder/builder.selector.ts - 121 + 112 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.html @@ -2801,6 +2883,26 @@ ่ฏทๅ‘้€ๆ•ฐๆฎๅˆฐ webhook ็ซฏ็‚นไปฅไพฟๆต‹่ฏ•ๆญฅ้ชคใ€‚ + + New Connection + + packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 2 + + + packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html + 3 + + + packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html + 3 + + ๆ–ฐๅปบ่ฟžๆŽฅ + Name is required @@ -2833,34 +2935,6 @@ ๅ็งฐๆ˜ฏๅฟ…ๅกซ้กน - - Reconnect - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 3 - - ้‡ๆ–ฐ่ฟžๆŽฅ - - - New Connection - - packages/ui/feature-connections/src/lib/dialogs/basic-auth-connection-dialog/basic-auth-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html - 2 - - - packages/ui/feature-connections/src/lib/dialogs/managed-oauth2-connection-dialog/managed-oauth2-connection-dialog.component.html - 3 - - - packages/ui/feature-connections/src/lib/dialogs/oauth2-connection-dialog/oauth2-connection-dialog.component.html - 3 - - ๆ–ฐๅปบ่ฟžๆŽฅ - Name can only contain letters, numbers and underscores @@ -2953,6 +3027,14 @@ ๆ˜ฏๅฟ…ๅกซ้กน + + Reconnect + + packages/ui/feature-connections/src/lib/dialogs/custom-auth-connection-dialog/custom-auth-connection-dialog.component.html + 3 + + ้‡ๆ–ฐ่ฟžๆŽฅ + You have to connect an account to continue @@ -3063,6 +3145,14 @@ ่ฏท็กฎไฟๆญค่ฟžๆŽฅ็š„ๅ‡ญๆฎๆ˜ฏๆญฃ็กฎ็š„ใ€‚ + + Home + + packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.html + 4 + + ้ฆ–้กต + Ask the Community @@ -3091,11 +3181,11 @@ Flows packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 82 + 92 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 119 + 69 ๆต็จ‹ @@ -3103,11 +3193,11 @@ Runs packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 105 + 101 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 35 + 29 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html @@ -3127,11 +3217,11 @@ Connections packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 111 + 107 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 111 + 61 packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.ts @@ -3143,11 +3233,11 @@ Team packages/ui/feature-dashboard/src/lib/components/sidenav-routes-list/sidenav-routes-list.component.ts - 117 + 113 packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 51 + 45 ๅ›ข้˜Ÿ @@ -3163,27 +3253,15 @@ Plans packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 43 + 37 ๅฅ—้ค - - Chatbot settings - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 70 - - - packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 92 - - Chatbot ่ฎพ็ฝฎ - Platform packages/ui/feature-dashboard/src/lib/dashboard.routing.ts - 131 + 81 ๅนณๅฐ @@ -3199,14 +3277,6 @@ ๅบ”็”จ - - Updated - - packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 33 - - ๅทฒๆ›ดๆ–ฐ - Delete Connection @@ -3219,7 +3289,7 @@ No connections created yet packages/ui/feature-dashboard/src/lib/pages/connections-table/connections-table.component.html - 58,60 + 59,61 ๅฐšๆœชๅˆ›ๅปบ่ฟžๆŽฅ @@ -3271,7 +3341,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table-title/flows-table-title.component.ts - 38 + 41 ๆœชๅ‘ฝๅ @@ -3347,11 +3417,11 @@ Real time flow packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 195 + 193 packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 202 + 200 ๅฎžๆ—ถๆต็จ‹ @@ -3362,7 +3432,7 @@ packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 199,201 + 197,199 ่ฟ่กŒ @@ -3373,7 +3443,7 @@ Please contact support as your published flow has a problem packages/ui/feature-dashboard/src/lib/pages/flows-table/flows-table.component.ts - 209 + 207 ็”ฑไบŽๆ‚จๅ‘ๅธƒ็š„ๆต็จ‹ๆœ‰้—ฎ้ข˜๏ผŒ่ฏท่”็ณปๅฎขๆœใ€‚ @@ -3567,11 +3637,23 @@ ๅทฒๅฎŒๆˆ + + + + + + packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html + 56,57 + + + + + No runs instantiated yet packages/ui/feature-dashboard/src/lib/pages/runs-table/runs-table.component.html - 49,51 + 72,74 ๅฐšๆ— ่ฟ่กŒๅฎžไพ‹ @@ -3583,6 +3665,14 @@ ๆ‚จๅฏไปฅๅœจ่ฟ™้‡Œๅฎ‰่ฃ…ๆ‚จ่‡ชๅทฑๆˆ–่ดก็Œฎ่€…ๅˆถไฝœ็š„ๅ—ใ€‚ + + Add Piece + + packages/ui/feature-pieces/src/lib/community-pieces-table/community-pieces-table.component.html + 29,31 + + ๆทปๅŠ ๅ— + No pieces installed @@ -3649,46 +3739,6 @@ ่ฏท็กฎไฟๆ‚จไฟกไปป่ฏฅๅ—็š„ไฝœ่€…๏ผŒๅฎƒๅฐ†ๆœ‰ๆƒ่ฎฟ้—ฎๆ‚จ็š„ๆต็จ‹ๆ•ฐๆฎ๏ผŒๅนถไธ”ๅฎƒๅฏ่ƒฝไธŽๅฝ“ๅ‰็‰ˆๆœฌ็š„ Activegesไธๅ…ผๅฎนใ€‚ - - Activepieces Team - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 15 - - Activepieces ๅ›ข้˜Ÿ - - - See less - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 36 - - ๆ”ถ่ตท - - - See more - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 39 - - ๅฑ•ๅผ€ - - - Use Template - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 57 - - ไฝฟ็”จ่ฏฅๆจก็‰ˆ - - - Verified icons created by lakonicon - Flaticon - - packages/ui/feature-templates/src/lib/featured-template-card/featured-template-card.component.html - 64,65 - - ็”ฑ lakonicon ๅˆ›ๅปบ็š„ๅทฒ้ชŒ่ฏๅ›พๆ ‡ - Flaticon - Learn about this flow @@ -3747,59 +3797,19 @@ ้€‰ๆ‹ฉ - - Featured - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 9 - - ็ฒพ้€‰ - - - Load more - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 23,25 - - ๅŠ ่ฝฝๆ›ดๅคš - - - All ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 41 - - ๆ‰€ๆœ‰ๅปบ่ฎฎ - Browse Templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 63 + 3 ๆต่งˆๆจกๆฟ - - A new way to explore fresh ideas from our partners - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 90 - - ๆŽข็ดขๅˆไฝœไผ™ไผดๆ–ฐๆƒณๆณ•็š„ๆ–ฐๆ–นๅผ - - - View featured ideas - - packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 92,93 - - ๆŸฅ็œ‹็ฒพ้€‰ๆ„่ง - Search templates packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 101 + 34 ๆœ็ดขๆจกๆฟ @@ -3807,19 +3817,17 @@ Start from scratch packages/ui/feature-templates/src/lib/templates-dialog/templates-dialog.component.html - 114 + 47 ไปŽๅคดๅผ€ๅง‹ - - Filters - + + Filters packages/ui/feature-templates/src/lib/templates-dialog/templates-filters/templates-filters.component.html - 1,3 + 2,4 - ็ญ›้€‰ - + ่ฟ‡ๆปคๅ™จ diff --git a/packages/ui/feature-authentication/src/lib/components/authentication-methods-separator/authentication-methods-separator.component.html b/packages/ui/feature-authentication/src/lib/components/authentication-methods-separator/authentication-methods-separator.component.html new file mode 100644 index 000000000..fd38f2e2d --- /dev/null +++ b/packages/ui/feature-authentication/src/lib/components/authentication-methods-separator/authentication-methods-separator.component.html @@ -0,0 +1,10 @@ + +
+
+
+ OR +
+
+
+
\ No newline at end of file diff --git a/packages/ui/feature-authentication/src/lib/components/authentication-methods-separator/authentication-methods-separator.component.ts b/packages/ui/feature-authentication/src/lib/components/authentication-methods-separator/authentication-methods-separator.component.ts new file mode 100644 index 000000000..63129025f --- /dev/null +++ b/packages/ui/feature-authentication/src/lib/components/authentication-methods-separator/authentication-methods-separator.component.ts @@ -0,0 +1,25 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { FlagService } from '@activepieces/ui/common'; +import { Observable } from 'rxjs'; +import { + ThirdPartyAuthnProvidersToShowMap, + ThirdPartyAuthnProviderEnum, +} from '@activepieces/ee-shared'; +import { ApFlagId } from '@activepieces/shared'; +@Component({ + selector: 'app-authentication-methods-separator', + templateUrl: './authentication-methods-separator.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AuthenticationMethodsSeparatorComponent { + readonly ThirdPartyAuthnProvider = ThirdPartyAuthnProviderEnum; + loginsWithEmailEnabled$: Observable; + thirdPartyProvidersToShow$: Observable; + constructor(private flagsService: FlagService) { + this.loginsWithEmailEnabled$ = this.flagsService.isFlagEnabled( + ApFlagId.EMAIL_AUTH_ENABLED + ); + this.thirdPartyProvidersToShow$ = + this.flagsService.getThirdPartyProvidersMap(); + } +} diff --git a/packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html b/packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html index 201c4fdf6..749cea047 100644 --- a/packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html +++ b/packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.html @@ -6,7 +6,9 @@ >
-
+ + Email @@ -26,6 +28,12 @@ You are not invited to any project, please contact your administrator. + + Your user is inactive, please contact your administrator to activate it. + + + Your email domain is not allowed to sign up, please contact your administrator. +
Please check your inbox to verify your account.
- +
@@ -46,7 +54,7 @@ Don't have an account? Sign up now
+ *ngIf="(isCommunityEdition$ | async )=== false && (loginsWithEmailEnabled$ | async)"> Forgot password?
diff --git a/packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.ts b/packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.ts index 94a33796a..59591e4fb 100644 --- a/packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.ts +++ b/packages/ui/feature-authentication/src/lib/pages/sign-in/sign-in.component.ts @@ -39,9 +39,13 @@ export class SignInComponent implements OnInit { isCommunityEdition$: Observable; showResendVerification = false; sendingVerificationEmail = false; + showDisabledUser = false; + domainIsNotAllowed = false; invitationOnlySignIn = false; + loginsWithEmailEnabled$: Observable; showSignUpLink$: Observable; sendVerificationEmail$?: Observable; + constructor( private formBuilder: FormBuilder, private authenticationService: AuthenticationService, @@ -50,6 +54,10 @@ export class SignInComponent implements OnInit { private snackbar: MatSnackBar, private route: ActivatedRoute, ) { + this.loginsWithEmailEnabled$ = this.flagsService.isFlagEnabled( + ApFlagId.EMAIL_AUTH_ENABLED + ); + this.showSignUpLink$ = this.flagsService.isFlagEnabled( ApFlagId.SHOW_SIGN_UP_LINK ); @@ -107,6 +115,8 @@ export class SignInComponent implements OnInit { this.showInvalidEmailOrPasswordMessage = false; this.showResendVerification = false; this.invitationOnlySignIn = false; + this.showDisabledUser = false; + this.domainIsNotAllowed = false; const request = this.loginForm.getRawValue(); this.authenticate$ = this.authenticationService.signIn(request).pipe( catchError((error: HttpErrorResponse) => { @@ -116,8 +126,12 @@ export class SignInComponent implements OnInit { if (error.status === StatusCodes.FORBIDDEN) { this.showResendVerification = error.error.code === ErrorCode.EMAIL_IS_NOT_VERIFIED; + this.showDisabledUser = + error.error.code === ErrorCode.USER_IS_INACTIVE; + this.domainIsNotAllowed = + error.error.code === ErrorCode.DOMAIN_NOT_ALLOWED; this.invitationOnlySignIn = - error.error.code === ErrorCode.INVITATIION_ONLY_SIGN_UP; + error.error.code === ErrorCode.INVITATION_ONLY_SIGN_UP; } this.loading = false; diff --git a/packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html b/packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html index 7b8d8496c..f9ea58fea 100644 --- a/packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html +++ b/packages/ui/feature-authentication/src/lib/pages/sign-up/sign-up.component.html @@ -4,7 +4,9 @@ Let's Get Started!
- + +
First Name @@ -58,6 +60,9 @@ You are not invited to any project, please contact your administrator. + + Your email domain is not allowed to sign up, please contact your administrator. +
; @@ -55,7 +55,9 @@ export class SignUpComponent implements OnInit { termsOfServiceUrl$: Observable; signUpDone = false; invitationOnlySignup = false; + domainIsNotAllowed = false; showNewsLetterCheckbox$: Observable; + emailLoginsEnabled$: Observable; readonly OtpType = OtpType; constructor( private formBuilder: FormBuilder, @@ -65,6 +67,9 @@ export class SignUpComponent implements OnInit { private router: Router, private activeRoute: ActivatedRoute ) { + this.emailLoginsEnabled$ = this.flagService.isFlagEnabled( + ApFlagId.EMAIL_AUTH_ENABLED + ); this.privacyPolicyUrl$ = this.flagService.getStringFlag( ApFlagId.PRIVACY_POLICY_URL ); @@ -92,20 +97,22 @@ export class SignUpComponent implements OnInit { ...this.registrationForm.getRawValue(), referringUserId, }; + this.invitationOnlySignup = false; + this.domainIsNotAllowed = false; this.signUp$ = this.authenticationService.signUp(request).pipe( tap((response) => { if ( response && response.body && response.body.token && - response.body.status === UserStatus.VERIFIED + response.body.verified ) { this.authenticationService.saveToken(response.body.token); this.authenticationService.saveUser(response); } }), tap((response) => { - if (response && response.body?.status === UserStatus.VERIFIED) { + if (response && response.body?.verified) { this.redirect(); } else { this.signUpDone = true; @@ -120,6 +127,12 @@ export class SignUpComponent implements OnInit { }); } this.invitationOnlySignup = err.status === HttpStatusCode.Forbidden; + if (err.status === StatusCodes.FORBIDDEN) { + this.invitationOnlySignup = + err.error.code === ErrorCode.INVITATION_ONLY_SIGN_UP; + this.domainIsNotAllowed = + err.error.code === ErrorCode.DOMAIN_NOT_ALLOWED; + } this.emailChanged = false; this.loading = false; return of(err); @@ -163,8 +176,6 @@ export class SignUpComponent implements OnInit { } case ApEdition.ENTERPRISE: return false; - default: - throw new UnhandledSwitchCaseError(ed); } }) ); diff --git a/packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts b/packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts index 6785563c8..e4d161f5f 100644 --- a/packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts +++ b/packages/ui/feature-authentication/src/lib/ui-feature-authentication.module.ts @@ -22,6 +22,7 @@ import { ResetPasswordComponent } from './pages/auth-actions/reset-password/rese import { ForgotPasswordComponent } from './pages/forgot-password/forgot-password.component'; import { EeComponentsModule } from '@activepieces/ee-components'; import { RedirectToDashboardIfLoggedIn } from './guards/redirect-to-dashboard-if-logged-in.guard'; +import { AuthenticationMethodsSeparatorComponent } from './components/authentication-methods-separator/authentication-methods-separator.component'; @NgModule({ imports: [ @@ -105,6 +106,7 @@ import { RedirectToDashboardIfLoggedIn } from './guards/redirect-to-dashboard-if VerifyEmailPostSignUpComponent, ResetPasswordComponent, ForgotPasswordComponent, + AuthenticationMethodsSeparatorComponent, ], }) export class UiFeatureAuthenticationModule {} diff --git a/packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/draw-common.ts b/packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/draw-common.ts index b784a0795..f8e29a198 100644 --- a/packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/draw-common.ts +++ b/packages/ui/feature-builder-canvas/src/lib/components/canvas-utils/drawing/draw-common.ts @@ -16,6 +16,7 @@ export type PositionLabel = { export const FLOW_ITEM_WIDTH = 300; export const FLOW_ITEM_HEIGHT = 92; export const FLOW_ITEM_BOTTOM_PADDING = 16; +/**Space between branch label and its next step */ export const VERTICAL_SPACE_BETWEEN_LABEL_AND_FLOW_ITEM = 70; export const FLOW_ITEM_HEIGHT_WITH_BOTTOM_PADDING = FLOW_ITEM_HEIGHT + FLOW_ITEM_BOTTOM_PADDING; @@ -26,6 +27,7 @@ export const HORIZONTAL_SPACE_BETWEEN_BRANCHES = 90; export const HORIZONTAL_SPACE_FOR_EMPTY_SIDE_OF_LOOP = 120; export const ARC_LENGTH = 15; export const EXTRA_VERTICAL_SPACE_FOR_LINE_WITH_LABEL = 25; +/**Space between branch label and its next small add button */ export const VERTICAL_SPACE_BETWEEN_LABEL_AND_BUTTON = EXTRA_VERTICAL_SPACE_FOR_LINE_WITH_LABEL * 1.25; export const BIG_BUTTON_SIZE = 40; diff --git a/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item-tree.component.ts b/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item-tree.component.ts index a7dc4b71d..304dca245 100755 --- a/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item-tree.component.ts +++ b/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item-tree.component.ts @@ -38,7 +38,7 @@ export class FlowItemTreeComponent implements OnInit { ngOnInit(): void { const flowVersion$ = this.store.select( - BuilderSelectors.selectShownFlowVersion + BuilderSelectors.selectViewedVersion ); this.flowDrawer$ = flowVersion$.pipe( map((version) => { diff --git a/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/duplicate-step-action/duplicate-step-action.component.ts b/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/duplicate-step-action/duplicate-step-action.component.ts index b1ece8d4e..7dd6ddab3 100644 --- a/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/duplicate-step-action/duplicate-step-action.component.ts +++ b/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/actions/duplicate-step-action/duplicate-step-action.component.ts @@ -41,7 +41,7 @@ export class DuplicateStepActionComponent { .select(BuilderSelectors.selectCurrentFlow) .pipe(take(1)); const currentFlowVersionId = this.store - .select(BuilderSelectors.selectCurrentFlowVersionId) + .select(BuilderSelectors.selectDraftVersionId) .pipe(take(1)); const flowVersion$: Observable = forkJoin({ currentFlowVersionId, diff --git a/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/big-add-button/big-add-button.component.html b/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/big-add-button/big-add-button.component.html index 42ef93ae0..8593546c2 100644 --- a/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/big-add-button/big-add-button.component.html +++ b/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/big-add-button/big-add-button.component.html @@ -10,10 +10,21 @@ - - - + + + +
+ + + + + + + + + +
diff --git a/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/big-add-button/big-add-button.component.ts b/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/big-add-button/big-add-button.component.ts index d9c826c30..e66e632dd 100644 --- a/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/big-add-button/big-add-button.component.ts +++ b/packages/ui/feature-builder-canvas/src/lib/components/flow-item-tree/flow-item/flow-item-connection/big-add-button/big-add-button.component.ts @@ -3,7 +3,12 @@ import { AddButtonCoreComponent } from '../add-button-core.component'; import { Store } from '@ngrx/store'; import { FlowRendererService } from '@activepieces/ui/feature-builder-store'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { BIG_BUTTON_SIZE } from '../../../../canvas-utils/drawing/draw-common'; +import { + BIG_BUTTON_SIZE, + FLOW_ITEM_BOTTOM_PADDING, + FLOW_ITEM_HEIGHT, +} from '../../../../canvas-utils/drawing/draw-common'; +import { SvgDrawer } from '../../../../canvas-utils/drawing/svg-drawer'; @Component({ selector: 'app-big-add-button', @@ -12,15 +17,33 @@ import { BIG_BUTTON_SIZE } from '../../../../canvas-utils/drawing/draw-common'; }) export class BigAddButtonComponent extends AddButtonCoreComponent { readonly BIG_BUTTON_SIZE = BIG_BUTTON_SIZE; + readonly PADDING_BETWEEN_BUTTON_AND_LINE = 12; showBoxShadow = false; @Input({ required: true }) inReadOnlyMode = false; - + /**the two lines above and below the add button that connect it to the graph */ + fillerLinesCommand = ''; + replacementLineWhenViewingRun = ''; constructor( store: Store, flowRendererService: FlowRendererService, snackbar: MatSnackBar ) { super(store, flowRendererService, snackbar); + const startX = BIG_BUTTON_SIZE / 2; + const startY = + -FLOW_ITEM_BOTTOM_PADDING - (FLOW_ITEM_HEIGHT - BIG_BUTTON_SIZE) / 2; + const lineLength = + FLOW_ITEM_BOTTOM_PADDING + (FLOW_ITEM_HEIGHT - BIG_BUTTON_SIZE) / 2; + this.fillerLinesCommand = SvgDrawer.empty() + .move(startX, startY) + .drawVerticalLine(lineLength - this.PADDING_BETWEEN_BUTTON_AND_LINE) + .move(startX, BIG_BUTTON_SIZE + this.PADDING_BETWEEN_BUTTON_AND_LINE) + .drawVerticalLine(lineLength) + .toSvg().content; + this.replacementLineWhenViewingRun = SvgDrawer.empty() + .move(startX, startY) + .drawVerticalLine(2 * lineLength + BIG_BUTTON_SIZE) + .toSvg().content; } } diff --git a/packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts b/packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts index 143e8a86e..6e0842b90 100755 --- a/packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts +++ b/packages/ui/feature-builder-form-controls/src/lib/code-artifact-form-control/code-artifact-control-fullscreen/code-artifact-control-fullscreen.component.ts @@ -152,7 +152,7 @@ export class CodeArtifactControlFullscreenComponent implements OnInit { const testCodeParams$ = forkJoin({ step: this.store.select(BuilderSelectors.selectCurrentStep).pipe(take(1)), flowVersionId: this.store - .select(BuilderSelectors.selectCurrentFlowVersionId) + .select(BuilderSelectors.selectDraftVersionId) .pipe(take(1)), }); diff --git a/packages/ui/feature-builder-form-controls/src/lib/piece-properties-form/auth-configs.pipe.ts b/packages/ui/feature-builder-form-controls/src/lib/piece-properties-form/auth-configs.pipe.ts index 4b235f8a3..6f53457c5 100644 --- a/packages/ui/feature-builder-form-controls/src/lib/piece-properties-form/auth-configs.pipe.ts +++ b/packages/ui/feature-builder-form-controls/src/lib/piece-properties-form/auth-configs.pipe.ts @@ -11,7 +11,7 @@ export class AuthConfigsPipe implements PipeTransform { pieceName: string ): ConnectionDropdownItem[] { return value.filter( - (item) => item.label.appName === pieceName || !item.label.appName + (item) => item.label.pieceName === pieceName || !item.label.pieceName ); } } diff --git a/packages/ui/feature-builder-form-controls/src/lib/piece-properties-form/selected-auth-config.pipe.ts b/packages/ui/feature-builder-form-controls/src/lib/piece-properties-form/selected-auth-config.pipe.ts index b7bf497c5..e93dab580 100644 --- a/packages/ui/feature-builder-form-controls/src/lib/piece-properties-form/selected-auth-config.pipe.ts +++ b/packages/ui/feature-builder-form-controls/src/lib/piece-properties-form/selected-auth-config.pipe.ts @@ -12,7 +12,9 @@ export class SelectedAuthConfigsPipe implements PipeTransform { controlValue: string ): ConnectionDropdownItem | undefined { return value - .filter((item) => item.label.appName === pieceName || !item.label.appName) + .filter( + (item) => item.label.pieceName === pieceName || !item.label.pieceName + ) .find((item) => item.value === controlValue); } } diff --git a/packages/ui/feature-builder-header/src/index.ts b/packages/ui/feature-builder-header/src/index.ts index 1bf159c78..543ad220f 100644 --- a/packages/ui/feature-builder-header/src/index.ts +++ b/packages/ui/feature-builder-header/src/index.ts @@ -4,6 +4,6 @@ export * from './lib/feedback/support.component'; export * from './lib/publish-button/publish-button.component'; export * from './lib/toggle-instance-state/toggle-instance-state.component'; export * from './lib/import-flow-dialogue/import-flow-dialogue.component'; -export * from './lib/draft-status/draft-status.component'; +export * from './lib/version-history-button/version-history-button.component'; import '@angular/localize/init'; diff --git a/packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html b/packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html deleted file mode 100644 index 200ccce0d..000000000 --- a/packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.html +++ /dev/null @@ -1,28 +0,0 @@ -
- -
- {{(draftStatus$ | async)}} -
-
-
- - - - -
-
- Switch Version -
-
- Draft -
-
- Published -
-
-
\ No newline at end of file diff --git a/packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts b/packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts deleted file mode 100644 index bce84238d..000000000 --- a/packages/ui/feature-builder-header/src/lib/draft-status/draft-status.component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { - BuilderSelectors, - ViewModeActions, - ViewModeEnum, -} from '@activepieces/ui/feature-builder-store'; -import { Observable, combineLatest, map } from 'rxjs'; - -@Component({ - selector: 'app-draft-status', - templateUrl: './draft-status.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class DraftStatusComponent { - draftStatus$: Observable; - isCurrentFlowVersionPublished$: Observable; - hasFlowBeenPublished$: Observable; - constructor(private store: Store) { - this.isCurrentFlowVersionPublished$ = this.store.select( - BuilderSelectors.selectIsCurrentVersionPublished - ); - this.hasFlowBeenPublished$ = this.store.select( - BuilderSelectors.selectHasFlowBeenPublished - ); - this.draftStatus$ = combineLatest({ - isCurrentVersionPublished: this.store.select( - BuilderSelectors.selectIsCurrentVersionPublished - ), - isInPublishedVersionView: this.store.select( - BuilderSelectors.selectIsInPublishedVersionViewMode - ), - }).pipe( - map((res) => { - if (res.isCurrentVersionPublished || res.isInPublishedVersionView) { - return $localize`Published`; - } - return $localize`Draft`; - }) - ); - } - - showPublishedVersion() { - this.store.dispatch( - ViewModeActions.setViewMode({ viewMode: ViewModeEnum.SHOW_PUBLISHED }) - ); - } - showDraftVersion() { - this.store.dispatch( - ViewModeActions.setViewMode({ viewMode: ViewModeEnum.BUILDING }) - ); - } -} diff --git a/packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html b/packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html index 1231160d6..3f9ed7343 100755 --- a/packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html +++ b/packages/ui/feature-builder-header/src/lib/flow-builder-header.component.html @@ -1,6 +1,4 @@