diff --git a/.all-contributorsrc b/.all-contributorsrc index 09effa6a78..546f299ab1 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1019,6 +1019,42 @@ "contributions": [ "plugin" ] + }, + { + "login": "LevwTech", + "name": "Abdelrahman Mostafa ", + "avatar_url": "https://avatars.githubusercontent.com/u/69399787?v=4", + "profile": "https://github.com/LevwTech", + "contributions": [ + "plugin" + ] + }, + { + "login": "HamzaZagha", + "name": "Hamza Zagha", + "avatar_url": "https://avatars.githubusercontent.com/u/45468866?v=4", + "profile": "https://github.com/HamzaZagha", + "contributions": [ + "bug" + ] + }, + { + "login": "founderblocks-sils", + "name": "Lasse Schuirmann", + "avatar_url": "https://avatars.githubusercontent.com/u/88160672?v=4", + "profile": "https://founderblocks.io/", + "contributions": [ + "plugin" + ] + }, + { + "login": "Startouf", + "name": "Cyril Duchon-Doris", + "avatar_url": "https://avatars.githubusercontent.com/u/7388889?v=4", + "profile": "https://about.me/cyril_duchon_doris", + "contributions": [ + "plugin" + ] } ], "commitType": "docs" diff --git a/Dockerfile b/Dockerfile index d7fed2b006..a636143756 100644 --- a/Dockerfile +++ b/Dockerfile @@ -49,8 +49,8 @@ COPY . . COPY .npmrc package.json package-lock.json ./ RUN npm ci -RUN npx nx run-many --target=build --projects=server-api --configuration production --skip-nx-cache -RUN npx nx run-many --target=build --projects=ui-core --configuration production --skip-nx-cache +RUN npx nx run-many --target=build --projects=server-api --configuration production +RUN npx nx run-many --target=build --projects=ui-core --configuration production # Install backend production dependencies RUN cd dist/packages/server/api && npm install --production --force diff --git a/README.md b/README.md index 8b9e159c81..691c2ee41e 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,12 @@ Not into coding but still interested in contributing? Come join our [Discord](ht Mohamed Hassan
Mohamed Hassan

๐Ÿ› Christian Schab
Christian Schab

๐Ÿ”Œ Pratik Kinage
Pratik Kinage

๐Ÿ”Œ + Abdelrahman Mostafa
Abdelrahman Mostafa

๐Ÿ”Œ + + + Hamza Zagha
Hamza Zagha

๐Ÿ› + Lasse Schuirmann
Lasse Schuirmann

๐Ÿ”Œ + Cyril Duchon-Doris
Cyril Duchon-Doris

๐Ÿ”Œ diff --git a/docker-compose.yml b/docker-compose.yml index dfaf607c43..325b914c33 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.0' services: activepieces: - image: ghcr.io/activepieces/activepieces:0.25.1 + image: ghcr.io/activepieces/activepieces:0.26.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/about/changelog.mdx b/docs/about/changelog.mdx index 134b3a417a..344cef5292 100755 --- a/docs/about/changelog.mdx +++ b/docs/about/changelog.mdx @@ -14,6 +14,171 @@ Semantic versioning is a standardized way of versioning software projects. It us Here's the revised text with improved grammar and spelling: +### 13/05/2024 + +**โœจ Exciting New Features** + +* feat: no-code friendly JSON viewer for steps output / input (#4586) @AbdulTheActivePiecer +* feat: add connections filters (by name and by piece) (#4507) @islamaf +* feat: increase webhook throughput via queued worker processing (#4543) @abuaboud +* feat: add listener to zoom on ctrl+mouse wheel (#4534) @islamaf +* feat: add platform role to user (#4481) @abuaboud + +**๐Ÿงฉ Pieces** + +* fix(notion): new updated trigger (#4670) @abuaboud +* feat(whatsapp-business): add sendMessage, sendMedia actions (#4639) @LevwTech +* feat(webhook): introduce production & testing url (#4635) @islamaf +* feat(openai): Extract Data from Text Action (#4636) @kishanprmr +* feat(wordpress): Add ACF field option for create/update post actions (#4637) @kishanprmr +* fix(trello): fix boards dropdown error (#4648) @kishanprmr +* feat(todoist): update task, find task and mark task completed action (#4641) @kishanprmr +* fix(github): load more than 100 repo (#4644) @kishanprmr +* feat(poper): New Lead Trigger (#4642) @thirstycode +* feat(zerobounce): validate email action (#4626) @abuaboud +* feat(queue): clear queue action (#4622) @AbdullahBitar +* feat(queue): pull items / push items into queue (#4620) @AbdullahBitar +* feat(slack): add reaction to message action (#4618) @kishanprmr +* feat(google-sheets): Updated Row Trigger Based on User Provided Column (#4604) @kishanprmr +* fix(webflow): show collection fields in all items actions (#4597) @kishanprmr +* feat(mailjet): send action (#4606) @christian-schab +* fix(apitable): Fix Boolean Field Not Updating Issue (#4603) @kishanprmr +* feat(segment): identify user (#4602) @abuaboud +* fix(moxie-crm): Fix Test Trigger Payload Issue (#4581) @kishanprmr +* fix(apitable): fail step on API error (#4577) @kishanprmr +* feat(google-calendar): Add Attendees to Event Action (#4579) @kishanprmr +* feat(google-calendar): event color option for create/update event action (#4572) @javix64 +* feat(wordpress): update post action (#4558) @kishanprmr +* feat(slack): add bot integration steps for channel actions/triggers (#4556) @kishanprmr +* feat(google-docs): find document action (#4557) @kishanprmr +* feat(gmail-dev): new email trigger in gmail developer edition piece (#4544) @kishanprmr +* feat(notion): find database item by property value action (#4546) @kishanprmr +* fix(store): fix append value action for RUN scope (#4545) @kishanprmr +* fix(google-sheets): fix missing option for other trigger (#4533) @AdamSelene + +**๐Ÿž Bug Fixes** + +* fix(webhook): distinguish test webhooks to run when flow not published/enabled (#4662) @islamaf +* fix: flow table pieces icons were being duplicated (#4656) @AbdulTheActivePiecer +* fix: continue flow on pause (#4651) @islamaf +* fix: change the step run duration to seconds (#4654) @islamaf +* fix: show dynamic input toggle for arrays (#4632) @AbdulTheActivePiecer +* fix: delay step were failing after loop (#4619) @abuaboud +* fix: github name can be nullable (#4593) @abuaboud +* fix: list cloud platform template (#4568) @abuaboud +* fix: list templates (#4567) @abuaboud +* fix: resolve platform id for managed login (#4566) @abuaboud +* fix: show data pills inside array inputs, instead of "Custom Code" (#4561) @AbdulTheActivePiecer +* fix: enhance runs list inside the builder by using better icons and making them clickable (#4560) @AbdulTheActivePiecer +* fix: allow copying and pasting data pills and undoing/redoing that (#4559) @AbdulTheActivePiecer +* fix: enhance panning in the builder using laptops touchpads (#4550) @AbdulTheActivePiecer + +**๐ŸŽจ Enhancements & Polish** + +* feat(webhook): introduce production & testing url (#4635) @islamaf +* feat: delete flow on git when deleted on user interface (#4615) @abuaboud +* chore: add different colors for step output values depending on their types. (#4596) @AbdulTheActivePiecer + +**๐Ÿ“š Documentation** + +* docs: add HamzaZagha as a contributor for bug (#4672) @allcontributors +* docs: add LevwTech as a contributor for plugin (#4653) @allcontributors +* docs: add thirstycode as a contributor for plugin (#4645) @allcontributors +* docs: add christian-schab as a contributor for plugin (#4608) @allcontributors +* docs: add MohamedHassan499 as a contributor for bug (#4594) @allcontributors +* docs: reset password instructions for sqlite3 (#4592) @abuaboud +* docs: add code snippets (#4589) @abuaboud +* docs: add javix64 as a contributor for plugin (#4582) @allcontributors + +**๐Ÿงน Maintenance & Dev Experience** + +* chore: security upgrade pdf-text-reader from 4.0.1 to 5.0.0 (#4643) @abuaboud +* chore(deps): bump pdfjs-dist and pdf-text-reader (#4646) @dependabot +* chore(deps): bump ejs from 3.1.9 to 3.1.10 (#4607) @dependabot +* chore(apollo): bump package json (#4609) @abuaboud +* chore: change table header background color (#4576) @abuaboud +* refactor: unify cloud database entities with enterprise (#4570) @abuaboud +* refactor: platform restrictions and login for platform (#4563) @abuaboud +* refactor: add logging for token expired (#4554) @abuaboud +* chore: add domain name on request trial + telemetry (#4553) @abuaboud + +**Thanks โค๏ธ** +@AbdulTheActivePiecer, @AbdullahBitar, @AdamSelene, @LevwTech, @abuaboud, @allcontributors, @allcontributors[bot], @christian-schab, @dependabot, @dependabot[bot], @islamaf, @javix64, @kishanprmr, @snyk-bot, @thirstycode and root + + +### 23/04/2024 + +**โœจ Exciting New Features** + +* feat(google-sheets): new and updated Row Trigger and Insert Multiple Rows Action (#4483) @kishanprmr +* feat(admin-console): release notifications & updates history (#4487) @islamaf +* feat: add report issue to connection dialog for unknown errors (#4465) @abuaboud +* feat: show updates in admin console (#4467) @islamaf + +**๐Ÿงฉ Pieces** + +* fix(notion): button field causing errors in rendering database properties (#4517) @kishanprmr +* feat(slack): Improve load sample data, find user by email, create channel and update profile action (#4498) @kishanprmr +* feat(google-sheets): New/Updated Row Trigger and Insert Multiple Rows Action (#4483) @kishanprmr +* feat(http): add proxy capabilities to the "send request" action (#4480) @pfernandez98 +* feat(straico): Ask AI and Custom API Call Action (#4489) @dennisrongo +* feat(google-drive): Move file and set file as public action (#4479) @pfernandez98 +* fix(google-drive): exclude folders from file polling (#4477) @AbdullahBitar +* feat(todoist): add section_id to create task action (#4456) @rendyt1 +* feat(google-drive): allow new file trigger to include content (#4342) @AbdullahBitar +* fix(todoist): wrong authorization header for custom API call (#4436) @rendyt1 +* fix(google-sheets): Add row number in response and fix find rows action (#4529) @kishanprmr + +**๐Ÿž Bug Fixes** + + +* fix: render array of properties (#4530) @AbdulTheActivePiecer +* fix(embed-sdk): stop opening new window in embedding after duplicating a flow (#4525) @AbdulTheActivePiecer +* fix: create new folder api (#4508) @abuaboud +* fix: retry update flow request (#4505) @abuaboud +* fix: inserting suggestions was always invalid (#4497) @AbdulTheActivePiecer +* fix(audit-logs): table had issues loading (#4490) @AbdulTheActivePiecer +* fix: hide unrequired auth for actions (#4491) @AbdulTheActivePiecer +* fix: triggering saving in read-only mode (#4473) @AbdulTheActivePiecer +* fix(embed-sdk): add connection iframe only when used (#4469) @AbdulTheActivePiecer +* fix(embed-sdk): allow copying and pasting inside embedding (#4468) @AbdulTheActivePiecer +* fix: export template not showing correct json (#4447) @abuaboud + +**๐ŸŽจ Enhancements & Polish** + +* chore: improve viewed dates in versions list and icons indicating version status (Draft/Published) (#4526) @AbdulTheActivePiecer +* feat: reordering items in the array property (#4513) @islamaf +* feat(embed-sdk): add custom navigation handler (#4485) @AbdulTheActivePiecer +* feat: new date and time format (#4502) @abuaboud +* chore: style copy webhook url button and add a better note for testing core webhook trigger (#4501) @AbdulTheActivePiecer +* feat: add folder name to flow api (#4499) @abuaboud +* feat: self serve custom domains (#4474) @islamaf +* fix: change reset zoom icon (#4478) @uniqueeest +* fix: more typos in the code (#4449) @szepeviktor + +**๐Ÿ“š Documentation** + +* docs(embed-sdk): add changelog (#4503) @abuaboud +* docs: improve building pieces section (#4509) @abuaboud +* docs: add uniqueeest as a contributor for code (#4486) @allcontributors +* docs: add islamaf as a contributor for bug (#4464) @allcontributors +* docs: add rendyt1 as a contributor for plugin (#4454) @allcontributors +* docs: add szepeviktor as a contributor for code (#4452) @allcontributors +* docs: fix typos & improve embedding documentation (#4448) @abuaboud +* docs: improve embed docs and document project pieces filter (#4433) @abuaboud + +**๐Ÿงน Maintenance & Dev Experience** + +* refactor: move sandbox files to server-worker (#4514) @abuaboud +* chore: remove unique email and platform null constraints (#4476) @abuaboud +* chore: security upgrade ngx-markdown from 16.0.0 to 17.0.0 (#4470) @abuaboud +* refactor: move all ui ee packages to folder ee/ui (#4466) @abuaboud +* fix: change pulumi project name in package.json (#4463) @islamaf + +**Thanks โค๏ธ** +@AbdulTheActivePiecer, @AbdullahBitar, @AdamSelene, @abuaboud, @allcontributors, @allcontributors[bot], @dennisrongo, @islamaf, @kishanprmr, @pfernandez98, @rendyt1, @snyk-bot, @szepeviktor, @tintinthedev and @uniqueeest + + ### 12/04/2024 diff --git a/package-lock.json b/package-lock.json index 1778664370..2df8803657 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "activepieces", - "version": "0.25.1", + "version": "0.26.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "activepieces", - "version": "0.25.1", + "version": "0.26.1", "dependencies": { "@angular/animations": "17.1.3", "@angular/cdk": "17.1.2", @@ -69,7 +69,7 @@ "axios": "1.6.7", "bcrypt": "5.1.1", "buffer": "6.0.3", - "bullmq": "5.3.3", + "bullmq": "5.4.2", "cheerio": "1.0.0-rc.12", "clarifai-nodejs-grpc": "9.7.0", "cli-table3": "0.6.3", @@ -147,7 +147,7 @@ "openai": "4.17.5", "papaparse": "5.4.1", "pdf-parse": "1.1.1", - "pdf-text-reader": "5.0.0", + "pdf-text-reader": "4.1.0", "pg": "8.11.3", "pickleparser": "0.1.0", "pino-loki": "2.1.3", @@ -11544,11 +11544,6 @@ "npm": ">= 8.6.0" } }, - "node_modules/@slack/web-api/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, "node_modules/@slack/web-api/node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -17028,6 +17023,30 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@verdaccio/auth": { + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-7.0.0-next-7.15.tgz", + "integrity": "sha512-BWexr0derpjjJh3fNh59aVen5pssvMTLRMTnBi9vmmn1Ndn6cOjeO6a16EhGixFdeef9YFrDMFRY7t96l4QrPQ==", + "dev": true, + "dependencies": { + "@verdaccio/config": "7.0.0-next-7.15", + "@verdaccio/core": "7.0.0-next-7.15", + "@verdaccio/loaders": "7.0.0-next-7.15", + "@verdaccio/logger": "7.0.0-next-7.15", + "@verdaccio/signature": "7.0.0-next-7.5", + "@verdaccio/utils": "7.0.0-next-7.15", + "debug": "4.3.4", + "lodash": "4.17.21", + "verdaccio-htpasswd": "12.0.0-next-7.15" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" + } + }, "node_modules/@verdaccio/commons-api": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@verdaccio/commons-api/-/commons-api-10.2.0.tgz", @@ -17046,13 +17065,13 @@ } }, "node_modules/@verdaccio/config": { - "version": "7.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-7.0.0-next-7.13.tgz", - "integrity": "sha512-QM0uvbLoK8IJMXCr9yYTlR9ZfoO26/sPt0ZGtzEp6yLZ4CZnI4C+eC6KlV/jPTSYf3s8GFAE9ZPvhnQyhX2DoA==", + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-7.0.0-next-7.15.tgz", + "integrity": "sha512-hXPfDakeyPz2YUo7ORGukKBqOPrNuOpohzWB1GSx6pNDYEepZiRbtpkOTNINiFbFbaXRU42co55PGEsMC3jyPg==", "dev": true, "dependencies": { - "@verdaccio/core": "7.0.0-next-7.13", - "@verdaccio/utils": "7.0.0-next-7.13", + "@verdaccio/core": "7.0.0-next-7.15", + "@verdaccio/utils": "7.0.0-next-7.15", "debug": "4.3.4", "js-yaml": "4.1.0", "lodash": "4.17.21", @@ -17110,9 +17129,9 @@ } }, "node_modules/@verdaccio/core": { - "version": "7.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-7.0.0-next-7.13.tgz", - "integrity": "sha512-95cSbuXc3GMaDjlSAEOkDIbN8YaYVq0E4yj/M6oIu4N4XK7DdbuB6k7YAr/TeUJU+2KFPHk3caDEj2a129jNCA==", + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-7.0.0-next-7.15.tgz", + "integrity": "sha512-BsClg5xGXZi755BvzYBrdOQOUNtyXyyslsnehGesy9ryKSRVSpGDi63/bZNHm10hMOkayPH5JE/tjtARX1AfRA==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -17169,6 +17188,24 @@ "url": "https://opencollective.com/verdaccio" } }, + "node_modules/@verdaccio/loaders": { + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/loaders/-/loaders-7.0.0-next-7.15.tgz", + "integrity": "sha512-X1lgV1DaXkPkEUJzqSZ6ojK4x2TJ+qUkzsyA9s6sBg6MxAe3bCxs9gOytEBA9fPy5f5nTXR63n9+EKaCgOLf2Q==", + "dev": true, + "dependencies": { + "@verdaccio/logger": "7.0.0-next-7.15", + "debug": "4.3.4", + "lodash": "4.17.21" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" + } + }, "node_modules/@verdaccio/local-storage-legacy": { "version": "11.0.2", "resolved": "https://registry.npmjs.org/@verdaccio/local-storage-legacy/-/local-storage-legacy-11.0.2.tgz", @@ -17210,13 +17247,30 @@ "node": ">=10" } }, + "node_modules/@verdaccio/logger": { + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-7.0.0-next-7.15.tgz", + "integrity": "sha512-Ch/dMJ5MV/gw18PFhFMZ0GyvRDzRctlL6XhQpP3p2ZFPiXVAqy/lgRZVQCk8UrKxZYgG6UVXGJMKJT827+esdw==", + "dev": true, + "dependencies": { + "@verdaccio/logger-commons": "7.0.0-next-7.15", + "pino": "8.17.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" + } + }, "node_modules/@verdaccio/logger-7": { - "version": "7.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-7/-/logger-7-7.0.0-next-7.13.tgz", - "integrity": "sha512-uiKIC6rSarSdYVdGKSw1JnDC04xCv6kKvqCjVV4GN9QeIaMzMBpPUZRwEEmq7Emc6tm+VPURVtbUy1aLRxWsmQ==", + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/logger-7/-/logger-7-7.0.0-next-7.15.tgz", + "integrity": "sha512-yC9WNI9TG5L/Q7J5zoVqRSZoZpbSiib5TL6jztufJ7UFsGz/2TU6f2Vny/w/Mmg6fVl4ddUQeaBnTRV0HDyriQ==", "dev": true, "dependencies": { - "@verdaccio/logger-commons": "7.0.0-next-7.13", + "@verdaccio/logger-commons": "7.0.0-next-7.15", "pino": "7.11.0" }, "engines": { @@ -17314,12 +17368,12 @@ } }, "node_modules/@verdaccio/logger-commons": { - "version": "7.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-7.0.0-next-7.13.tgz", - "integrity": "sha512-C45E+e/yMc54hXzRkiUZjl/fXObfcAGE1EMXxZjIqjPUx4gnAWEuTpT1NVRxZbMU6HdpOOKrgijwYkBpo5pgCg==", + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-7.0.0-next-7.15.tgz", + "integrity": "sha512-MeAaU2IMdZSwdO/hrh7aTg1ax3iKlPf6eLVf0JpNYKDxN8OCsi2o5+Q014rGyEG8++Pri3D4DIxMJA7TA+t15g==", "dev": true, "dependencies": { - "@verdaccio/core": "7.0.0-next-7.13", + "@verdaccio/core": "7.0.0-next-7.15", "@verdaccio/logger-prettify": "7.0.0-next-7.2", "colorette": "2.0.20", "debug": "4.3.4" @@ -17358,16 +17412,44 @@ "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", "dev": true }, + "node_modules/@verdaccio/logger/node_modules/pino": { + "version": "8.17.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", + "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", + "dev": true, + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.1.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.7.0", + "thread-stream": "^2.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/@verdaccio/logger/node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "dev": true + }, "node_modules/@verdaccio/middleware": { - "version": "7.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-7.0.0-next-7.13.tgz", - "integrity": "sha512-SwStOZeOJ9GFgJEeOhXP3AZpx35HNl66Ru8zKUb0VrOw8mna/4d+/wL+5bAaYfG/k5uO1eOJoTBMQLJkFfQyZA==", + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-7.0.0-next-7.15.tgz", + "integrity": "sha512-54VA3/TbHpb7gIaq3RV9nqR6s4FtuKa5gnpwJEwU/SCdZrZiS2r6+doeQQz96xthrFzpBS1rp0IrRCcRcDs/Uw==", "dev": true, "dependencies": { - "@verdaccio/config": "7.0.0-next-7.13", - "@verdaccio/core": "7.0.0-next-7.13", - "@verdaccio/url": "12.0.0-next-7.13", - "@verdaccio/utils": "7.0.0-next-7.13", + "@verdaccio/config": "7.0.0-next-7.15", + "@verdaccio/core": "7.0.0-next-7.15", + "@verdaccio/url": "12.0.0-next-7.15", + "@verdaccio/utils": "7.0.0-next-7.15", "debug": "4.3.4", "express": "4.18.3", "express-rate-limit": "5.5.1", @@ -17546,16 +17628,16 @@ } }, "node_modules/@verdaccio/signature": { - "version": "7.0.0-next.3", - "resolved": "https://registry.npmjs.org/@verdaccio/signature/-/signature-7.0.0-next.3.tgz", - "integrity": "sha512-egs1VmEe+COUUZ83I6gzDy79Jo3b/AExPvp9EDuJHkmwxJj+9gb231Rv4wk+UoNPrQRNLljUepQwVrDmbqP5DQ==", + "version": "7.0.0-next-7.5", + "resolved": "https://registry.npmjs.org/@verdaccio/signature/-/signature-7.0.0-next-7.5.tgz", + "integrity": "sha512-xF0xGi10HOAQ7Mkwf6dC2fjaBrdxxqXE/HMh/l/O5/LpWoGFZ6xsm/3ZieVRJtIq/qvL5pmmO5Tn8lPS7pm5SQ==", "dev": true, "dependencies": { "debug": "4.3.4", "jsonwebtoken": "9.0.2" }, "engines": { - "node": ">=12" + "node": ">=14" }, "funding": { "type": "opencollective", @@ -17620,14 +17702,14 @@ } }, "node_modules/@verdaccio/tarball": { - "version": "12.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-12.0.0-next-7.13.tgz", - "integrity": "sha512-O74anmOdpeUL52LtYRso8UQMKKRqDsnEaTLRACOEQevhyBp/ySs2XwVLPoHymDaXiUQfKUP06HekujdedTpO+A==", + "version": "12.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-12.0.0-next-7.15.tgz", + "integrity": "sha512-wjAbLHUxg9FxVmGoW+qvLbv2eWy61MrRkJQWm2+1Zq4JBC6BdKsGZ3AXrpEc+MYi3U1b7Nmi28zXJ9gJ0/HaLQ==", "dev": true, "dependencies": { - "@verdaccio/core": "7.0.0-next-7.13", - "@verdaccio/url": "12.0.0-next-7.13", - "@verdaccio/utils": "7.0.0-next-7.13", + "@verdaccio/core": "7.0.0-next-7.15", + "@verdaccio/url": "12.0.0-next-7.15", + "@verdaccio/utils": "7.0.0-next-7.15", "debug": "4.3.4", "lodash": "4.17.21" }, @@ -17640,18 +17722,18 @@ } }, "node_modules/@verdaccio/ui-theme": { - "version": "7.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-7.0.0-next-7.13.tgz", - "integrity": "sha512-qRIGVahav3y70fIX35iqUxvyzhJ6fQmGZP+b0ODu0zCOn3zFCwS5bkTXuRITIACo26pZIMauw9LTqqsb1GPmLA==", + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-7.0.0-next-7.15.tgz", + "integrity": "sha512-4kQr+OKTe+j1ZNBukBsQ4x1GwkM+3qfVuLk0fdGCjPRL+hf6o6piTgIrXsujcJDzSx+lQL6KEqkrmAUsHhjKag==", "dev": true }, "node_modules/@verdaccio/url": { - "version": "12.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-12.0.0-next-7.13.tgz", - "integrity": "sha512-Ag2sF4Q6DewKJtFIayo21KCgp9azdfsgvgjM8nlQkuWduHYgack5w/paTX5z2hfyFtbXyO648DvBBvmkjcBjbA==", + "version": "12.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-12.0.0-next-7.15.tgz", + "integrity": "sha512-VyfRKdQv3Urbj8sgUp3xfnm85EHtiTrco1Ve9UbXB0u0SfSpOihUw3TfFzUjLfkyeZE8oBJ8JLZIKmkOm9ZF+w==", "dev": true, "dependencies": { - "@verdaccio/core": "7.0.0-next-7.13", + "@verdaccio/core": "7.0.0-next-7.15", "debug": "4.3.4", "lodash": "4.17.21", "validator": "13.11.0" @@ -17664,13 +17746,22 @@ "url": "https://opencollective.com/verdaccio" } }, + "node_modules/@verdaccio/url/node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/@verdaccio/utils": { - "version": "7.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-7.0.0-next-7.13.tgz", - "integrity": "sha512-ZtQZjUyxHZHQSjbajncdPLuJt5D0k3r6RQ8Wg0jM5LAoAJM5L+ISC0lcDZI3VRY6mq/LhU6hKfUygBK8o7VQmQ==", + "version": "7.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-7.0.0-next-7.15.tgz", + "integrity": "sha512-J0X/SFiCgty5hSI9ghjj4ZG5nf6+txfVWGzuFjlR3UPP1VvpqTu+oya/45sBwZcC/uvfm1LwKCT6tVbcQYlScg==", "dev": true, "dependencies": { - "@verdaccio/core": "7.0.0-next-7.13", + "@verdaccio/core": "7.0.0-next-7.15", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.0" @@ -19614,15 +19705,13 @@ } }, "node_modules/bullmq": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.3.3.tgz", - "integrity": "sha512-Gc/68HxiCHLMPBiGIqtINxcf8HER/5wvBYMY/6x3tFejlvldUBFaAErMTLDv4TnPsTyzNPrfBKmFCEM58uVnJg==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.4.2.tgz", + "integrity": "sha512-dkR/KGUw18miLe3QWtvSlmGvEe08aZF+w1jZyqEHMWFW3RP4162qp6OGud0/QCAOjusiRI8UOxUhbnortPY+rA==", "dependencies": { "cron-parser": "^4.6.0", - "fast-glob": "^3.3.2", "ioredis": "^5.3.2", "lodash": "^4.17.21", - "minimatch": "^9.0.3", "msgpackr": "^1.10.1", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", @@ -19630,43 +19719,6 @@ "uuid": "^9.0.0" } }, - "node_modules/bullmq/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/bullmq/node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/bullmq/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/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -21005,19 +21057,6 @@ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" }, - "node_modules/cookies": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", - "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", - "dev": true, - "dependencies": { - "depd": "~2.0.0", - "keygrip": "~1.1.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/copy-anything": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", @@ -23354,9 +23393,9 @@ } }, "node_modules/envinfo": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz", - "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", + "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", "dev": true, "bin": { "envinfo": "dist/cli.js" @@ -24227,9 +24266,9 @@ } }, "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, "node_modules/events": { "version": "3.3.0", @@ -26593,6 +26632,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/http-proxy/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, "node_modules/http-server": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", @@ -29731,18 +29776,6 @@ "node": ">= 12" } }, - "node_modules/keygrip": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", - "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", - "dev": true, - "dependencies": { - "tsscmp": "1.0.6" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -30768,12 +30801,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/listr2/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true - }, "node_modules/listr2/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -34152,6 +34179,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -34473,13 +34505,13 @@ "node": ">=8" } }, - "node_modules/path2d": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.0.tgz", - "integrity": "sha512-KdPAykQX6kmLSOO6Jpu2KNcCED7CKjmaBNGGNuctOsG0hgYO1OdYQaan6cYXJiG0WmXOwZZPILPBimu5QAIw3A==", + "node_modules/path2d-polyfill": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz", + "integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==", "optional": true, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/pause-stream": { @@ -34512,23 +34544,23 @@ } }, "node_modules/pdf-text-reader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pdf-text-reader/-/pdf-text-reader-5.0.0.tgz", - "integrity": "sha512-OthaaaSutojBNll4LMiD4oMRP2RoRxA7FQamvBGr9LFoXa2R5E2rwaKXrABddy/O+I7nAR5qrGO/pOjZrMNvog==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pdf-text-reader/-/pdf-text-reader-4.1.0.tgz", + "integrity": "sha512-De2EnUEI+w626PNHMY01Yqdafr6GG5Il8cIplgKsthsc4Uoailq6LNbBhAoFcvLCwzgS/HX/5jajXSL0A0tTcw==", "dependencies": { - "pdfjs-dist": "4.2.67" + "pdfjs-dist": "3.9.179" } }, "node_modules/pdfjs-dist": { - "version": "4.2.67", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.2.67.tgz", - "integrity": "sha512-rJmuBDFpD7cqC8WIkQUEClyB4UAH05K4AsyewToMTp2gSy3Rrx8c1ydAVqlJlGv3yZSOrhEERQU/4ScQQFlLHA==", + "version": "3.9.179", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.9.179.tgz", + "integrity": "sha512-AZBEIAORYDaOAlM0/A4Zg465+XF3ugYDdgrVmioVvNW5tH3xs3RpGFBYOG5PM9/vLM3M/wNncsMLTgyIKdqMKg==", "engines": { "node": ">=18" }, "optionalDependencies": { "canvas": "^2.11.2", - "path2d": "^0.2.0" + "path2d-polyfill": "^2.0.1" } }, "node_modules/peberminta": { @@ -41363,15 +41395,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "dev": true, - "engines": { - "node": ">=0.6.x" - } - }, "node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", @@ -42285,9 +42308,9 @@ } }, "node_modules/validator": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", - "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", "dev": true, "engines": { "node": ">= 0.10" @@ -42310,32 +42333,32 @@ } }, "node_modules/verdaccio": { - "version": "5.30.3", - "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-5.30.3.tgz", - "integrity": "sha512-s/ZhSRBusW2o+ZkERyzEIbVL3zo8QLpTQPVoB/pn/Yv6+ngflP+anK4xCYiXXQJhqEdBz3cwApa8UgOEaNSS4Q==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-5.31.0.tgz", + "integrity": "sha512-jqBUlvFVArgv5AwtrwUQHDEI9rJHbr8YhA+Wzl56hBZ3Egso9dG9XUiDV+Pbl0yjf7CFghKKuWtQ2Bo6neZXqw==", "dev": true, "dependencies": { "@cypress/request": "3.0.1", - "@verdaccio/config": "7.0.0-next-7.13", - "@verdaccio/core": "7.0.0-next-7.13", + "@verdaccio/auth": "7.0.0-next-7.15", + "@verdaccio/config": "7.0.0-next-7.15", + "@verdaccio/core": "7.0.0-next-7.15", "@verdaccio/local-storage-legacy": "11.0.2", - "@verdaccio/logger-7": "7.0.0-next-7.13", - "@verdaccio/middleware": "7.0.0-next-7.13", + "@verdaccio/logger-7": "7.0.0-next-7.15", + "@verdaccio/middleware": "7.0.0-next-7.15", "@verdaccio/search-indexer": "7.0.0-next-7.2", - "@verdaccio/signature": "7.0.0-next.3", + "@verdaccio/signature": "7.0.0-next-7.5", "@verdaccio/streams": "10.2.1", - "@verdaccio/tarball": "12.0.0-next-7.13", - "@verdaccio/ui-theme": "7.0.0-next-7.13", - "@verdaccio/url": "12.0.0-next-7.13", - "@verdaccio/utils": "7.0.0-next-7.13", + "@verdaccio/tarball": "12.0.0-next-7.15", + "@verdaccio/ui-theme": "7.0.0-next-7.15", + "@verdaccio/url": "12.0.0-next-7.15", + "@verdaccio/utils": "7.0.0-next-7.15", "async": "3.2.5", "clipanion": "3.2.1", "compression": "1.7.4", - "cookies": "0.9.1", "cors": "2.8.5", "debug": "^4.3.4", - "envinfo": "7.11.1", - "express": "4.18.3", + "envinfo": "7.13.0", + "express": "4.19.2", "express-rate-limit": "5.5.1", "fast-safe-stringify": "2.1.1", "handlebars": "4.7.8", @@ -42349,10 +42372,10 @@ "mkdirp": "1.0.4", "mv": "2.1.1", "pkginfo": "0.4.1", - "semver": "7.6.0", - "validator": "13.11.0", - "verdaccio-audit": "12.0.0-next-7.13", - "verdaccio-htpasswd": "12.0.0-next-7.13" + "semver": "7.6.2", + "validator": "13.12.0", + "verdaccio-audit": "12.0.0-next-7.15", + "verdaccio-htpasswd": "12.0.0-next-7.15" }, "bin": { "verdaccio": "bin/verdaccio" @@ -42366,13 +42389,13 @@ } }, "node_modules/verdaccio-audit": { - "version": "12.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-12.0.0-next-7.13.tgz", - "integrity": "sha512-vmq0DQjgieEV0oXmNKeQQKLxNSOlWmDP3o4tDSH39o54aDLZLfQ9+xUpZjguJxWvDjZyWmBCpHnJdjAyp2VAiA==", + "version": "12.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-12.0.0-next-7.15.tgz", + "integrity": "sha512-ylUxj3VZljYyCpAUFa3THFb29UyHCVv8qgte0LI/20+5EptcZuayHtVP5sd5mnMiMqCO4TUylm30EdXSIvqk4A==", "dev": true, "dependencies": { - "@verdaccio/config": "7.0.0-next-7.13", - "@verdaccio/core": "7.0.0-next-7.13", + "@verdaccio/config": "7.0.0-next-7.15", + "@verdaccio/core": "7.0.0-next-7.15", "express": "4.18.3", "https-proxy-agent": "5.0.1", "node-fetch": "cjs" @@ -42505,12 +42528,12 @@ ] }, "node_modules/verdaccio-htpasswd": { - "version": "12.0.0-next-7.13", - "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-12.0.0-next-7.13.tgz", - "integrity": "sha512-1xhKuDYRTPHv1NPeTb83thkApmM1zrvKz9pZk4F6rjlWfkIO1pBslaRXO/Qo6VrvCRSIaw+Qqbci86GCyFaLKg==", + "version": "12.0.0-next-7.15", + "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-12.0.0-next-7.15.tgz", + "integrity": "sha512-m8yXFdYi8FQfP9VeZA3Rdecgkn3QWeeMVEV7bA49w0rpC2DBgOfUcKGNMfZZL4C4gv8M3spCZgJH2adKEFbfbw==", "dev": true, "dependencies": { - "@verdaccio/core": "7.0.0-next-7.13", + "@verdaccio/core": "7.0.0-next-7.15", "@verdaccio/file-locking": "12.0.0-next.1", "apache-md5": "1.1.8", "bcryptjs": "2.4.3", @@ -42560,105 +42583,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/verdaccio/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/verdaccio/node_modules/express": { - "version": "4.18.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", - "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==", - "dev": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/verdaccio/node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/verdaccio/node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/verdaccio/node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verdaccio/node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/verdaccio/node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/verdaccio/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -42756,21 +42680,6 @@ "node": ">=10" } }, - "node_modules/verdaccio/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/verdaccio/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -42791,6 +42700,18 @@ } ] }, + "node_modules/verdaccio/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/package.json b/package.json index dda3b1bb62..0ff07edfe0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "activepieces", - "version": "0.25.1", - "rcVersion": "0.26.0-rc.3", + "version": "0.26.1", + "rcVersion": "0.27.0-rc.0", "scripts": { "prepare": "husky install", "serve:frontend": "nx serve ui-core", @@ -82,7 +82,7 @@ "axios": "1.6.7", "bcrypt": "5.1.1", "buffer": "6.0.3", - "bullmq": "5.3.3", + "bullmq": "5.4.2", "cheerio": "1.0.0-rc.12", "clarifai-nodejs-grpc": "9.7.0", "cli-table3": "0.6.3", @@ -160,7 +160,7 @@ "openai": "4.17.5", "papaparse": "5.4.1", "pdf-parse": "1.1.1", - "pdf-text-reader": "5.0.0", + "pdf-text-reader": "4.1.0", "pg": "8.11.3", "pickleparser": "0.1.0", "pino-loki": "2.1.3", diff --git a/packages/pieces/community/clickup/package.json b/packages/pieces/community/clickup/package.json index 8b422a21da..42ab8852d1 100644 --- a/packages/pieces/community/clickup/package.json +++ b/packages/pieces/community/clickup/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-clickup", - "version": "0.5.11" + "version": "0.5.12" } \ No newline at end of file diff --git a/packages/pieces/community/clickup/src/lib/actions/comments/create-task-comment.ts b/packages/pieces/community/clickup/src/lib/actions/comments/create-task-comment.ts index 7b9eef820c..8c4488c3db 100644 --- a/packages/pieces/community/clickup/src/lib/actions/comments/create-task-comment.ts +++ b/packages/pieces/community/clickup/src/lib/actions/comments/create-task-comment.ts @@ -18,19 +18,30 @@ export const createClickupTaskComment = createAction({ displayName: 'Comment', required: true, }), + assignee_id: clickupCommon.single_assignee_id( + false, + 'Assignee Id', + 'ID of assignee for Task Comment' + ), }, async run(configValue) { const { task_id, comment } = configValue.propsValue; - const user_request = await callClickUpApi( - HttpMethod.GET, - `/user`, - getAccessTokenOrThrow(configValue.auth), - {} - ); + let assignee_id = configValue.propsValue.assignee_id; + + if (!assignee_id) { + const user_request = await callClickUpApi( + HttpMethod.GET, + `/user`, + getAccessTokenOrThrow(configValue.auth), + {} + ); + + if (user_request.body['user'] === undefined) { + throw 'Please connect to your ClickUp account'; + } - if (user_request.body['user'] === undefined) { - throw 'Please connect to your ClickUp account'; + assignee_id = user_request.body['user']['id']; } const response = await callClickUpApi( @@ -39,7 +50,7 @@ export const createClickupTaskComment = createAction({ getAccessTokenOrThrow(configValue.auth), { comment_text: comment, - assignee: user_request.body['user']['id'], + assignee: assignee_id, notify_all: true, } ); diff --git a/packages/pieces/community/clickup/src/lib/common/index.ts b/packages/pieces/community/clickup/src/lib/common/index.ts index 1313e4533c..e8853f0a9f 100644 --- a/packages/pieces/community/clickup/src/lib/common/index.ts +++ b/packages/pieces/community/clickup/src/lib/common/index.ts @@ -45,7 +45,7 @@ export const clickupCommon = { }, }), space_id: (required = true, multi = false) => { - const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown + const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown; return Dropdown({ description: 'The ID of the ClickUp space to create the task in', displayName: 'Space', @@ -71,10 +71,10 @@ export const clickupCommon = { }), }; }, - }) + }); }, list_id: (required = true, multi = false) => { - const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown + const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown; return Dropdown({ description: 'The ID of the ClickUp space to create the task in', displayName: 'List', @@ -88,9 +88,12 @@ export const clickupCommon = { options: [], }; } - + const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue); - const lists: {name:string, id:string}[] = await listAllLists(accessToken, space_id as string) + const lists: { name: string; id: string }[] = await listAllLists( + accessToken, + space_id as string + ); return { disabled: false, @@ -102,9 +105,9 @@ export const clickupCommon = { }), }; }, - }) + }); }, - task_id: (required=true, label:string|undefined = undefined) => + task_id: (required = true, label: string | undefined = undefined) => Property.Dropdown({ description: 'The ID of the ClickUp task', displayName: label ?? 'Task Id', @@ -134,7 +137,7 @@ export const clickupCommon = { }, }), folder_id: (required = false, multi = false) => { - const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown + const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown; return Dropdown({ description: 'The ID of the ClickUp folder', displayName: 'Folder Id', @@ -161,7 +164,7 @@ export const clickupCommon = { }), }; }, - }) + }); }, field_id: (required = false) => Property.Dropdown({ @@ -174,13 +177,15 @@ export const clickupCommon = { if (!auth || !task_id || !list_id) { return { disabled: true, - placeholder: - 'connect your account first and select a task', + placeholder: 'connect your account first and select a task', options: [], }; } const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue); - const response = await listAccessibleCustomFields(accessToken, list_id as string); + const response = await listAccessibleCustomFields( + accessToken, + list_id as string + ); return { disabled: false, options: response.fields.map((field) => { @@ -193,7 +198,7 @@ export const clickupCommon = { }, }), status_id: (required = false, multi = false) => { - const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown + const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown; return Dropdown({ description: 'The ID of Clickup Issue Status', displayName: 'Status Id', @@ -226,7 +231,7 @@ export const clickupCommon = { }), }; }, - }) + }); }, priority_id: (required = false) => Property.StaticDropdown({ @@ -296,6 +301,47 @@ export const clickupCommon = { }; }, }), + single_assignee_id: ( + required = false, + displayName = 'Assignee Id', + description: string + ) => + Property.Dropdown({ + displayName: displayName, + description: description, + required, + refreshers: ['workspace_id'], + options: async ({ auth, workspace_id }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'conncet your account first', + options: [], + }; + } + if (!workspace_id) { + return { + disabled: true, + placeholder: 'select workspace', + options: [], + }; + } + const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue); + const response = await listWorkspaceMembers( + accessToken, + workspace_id as string + ); + return { + disabled: false, + options: response.map((member) => { + return { + label: member.user.username, + value: member.user.id, + }; + }), + }; + }, + }), template_id: (required = false) => Property.Dropdown({ displayName: 'Template Id', @@ -388,10 +434,7 @@ export async function listSpaces(accessToken: string, workspaceId: string) { } export async function listAllLists(accessToken: string, spaceId: string) { - const responseFolders = await listFolders( - accessToken, - spaceId as string - ); + const responseFolders = await listFolders(accessToken, spaceId as string); const promises: Promise<{ lists: { id: string; name: string }[] }>[] = [ listFolderlessList(accessToken, spaceId as string), ]; @@ -405,7 +448,7 @@ export async function listAllLists(accessToken: string, spaceId: string) { lists = [...lists, ...listsResponses[i].lists]; } - return lists + return lists; } export async function listLists(accessToken: string, folderId: string) { @@ -440,22 +483,24 @@ export async function listFolders(accessToken: string, spaceId: string) { ).body; } -export async function listAccessibleCustomFields(accessToken: string, listId: string) { +export async function listAccessibleCustomFields( + accessToken: string, + listId: string +) { return ( await callClickUpApi<{ fields: { id: string; name: string; type: string; - type_config: Record + type_config: Record; date_created: string; - hide_from_guests: false + hide_from_guests: false; }[]; }>(HttpMethod.GET, `list/${listId}/field`, accessToken, undefined) ).body; } - async function listFolderlessList(accessToken: string, spaceId: string) { return ( await callClickUpApi<{ @@ -504,9 +549,9 @@ export async function callClickUpApi( method: HttpMethod, apiUrl: string, accessToken: string, - body: any|undefined, - queryParams: any|undefined = undefined, - headers: any|undefined = undefined + body: any | undefined, + queryParams: any | undefined = undefined, + headers: any | undefined = undefined ): Promise> { return await httpClient.sendRequest({ method: method, @@ -517,6 +562,6 @@ export async function callClickUpApi( }, headers, body, - queryParams - }) + queryParams, + }); } diff --git a/packages/pieces/community/formbricks/package.json b/packages/pieces/community/formbricks/package.json index e9859c29e1..68b654247a 100644 --- a/packages/pieces/community/formbricks/package.json +++ b/packages/pieces/community/formbricks/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-formbricks", - "version": "0.0.5" + "version": "0.1.0" } \ No newline at end of file diff --git a/packages/pieces/community/formbricks/src/index.ts b/packages/pieces/community/formbricks/src/index.ts index 2c752b3057..7fd33288a4 100644 --- a/packages/pieces/community/formbricks/src/index.ts +++ b/packages/pieces/community/formbricks/src/index.ts @@ -1,5 +1,5 @@ import { createCustomApiCallAction } from '@activepieces/pieces-common'; -import { createPiece, PieceAuth } from '@activepieces/pieces-framework'; +import { createPiece, PieceAuth, Property } from '@activepieces/pieces-framework'; import { PieceCategory } from '@activepieces/shared'; import { triggers } from './lib/triggers'; @@ -10,16 +10,41 @@ const markdownPropertyDescription = ` 3. Select 'Product Settings' 4. On the left, select 'API Keys' 5. Click on 'Add Production API Key' - 6. On the popup form, Enter the 'API Key Label' to name the Key + 6. On the popup form, enter the 'API Key Label' to name the Key 7. Copy the API key and paste it below. + + **APP URL:** + - The API URL for Formbricks example the cloud is at https://app.formbricks.com + - **Note: make sure there is no trailing slash and no /api** `; -export const formBricksAuth = PieceAuth.SecretText({ - displayName: 'Token', - description: markdownPropertyDescription, +export type FormBricksAuthType = { + appUrl: string; + apiKey: string; +} + +export const formBricksAuth = PieceAuth.CustomAuth({ required: true, + description: markdownPropertyDescription, + props: { + appUrl: Property.ShortText({ + displayName: 'APP URL', + required: true, + defaultValue: 'https://app.formbricks.com', + }), + apiKey: Property.ShortText({ + displayName: 'API Key', + required: true, + }), + }, + validate: async () => { + return { + valid: true + } + }, }); + export const formbricks = createPiece({ displayName: 'Formbricks', description: 'Open source Survey Platform', @@ -33,10 +58,10 @@ export const formbricks = createPiece({ auth: formBricksAuth, authMapping(auth) { return { - 'x-Api-Key': auth as string, + 'x-Api-Key': (auth as FormBricksAuthType).apiKey, }; }, - baseUrl: () => 'https://api.formbricks.com/v1', + baseUrl: (auth) => `${(auth as FormBricksAuthType).appUrl}/api/v1`, }), ], triggers, diff --git a/packages/pieces/community/formbricks/src/lib/triggers/register.ts b/packages/pieces/community/formbricks/src/lib/triggers/register.ts index 3ea63dd372..e6399d452a 100644 --- a/packages/pieces/community/formbricks/src/lib/triggers/register.ts +++ b/packages/pieces/community/formbricks/src/lib/triggers/register.ts @@ -8,7 +8,7 @@ import { HttpRequest, HttpMethod, } from '@activepieces/pieces-common'; -import { formBricksAuth } from '../..'; +import { FormBricksAuthType, formBricksAuth } from '../..'; export const formBricksRegisterTrigger = ({ name, @@ -46,7 +46,7 @@ export const formBricksRegisterTrigger = ({ const response = await httpClient.sendRequest<{ data: Survey[] }>({ method: HttpMethod.GET, - url: `https://app.formbricks.com/api/v1/management/surveys`, + url: `${(auth as FormBricksAuthType).appUrl}/api/v1/management/surveys`, headers: { 'x-Api-Key': auth as unknown as string, }, @@ -77,7 +77,7 @@ export const formBricksRegisterTrigger = ({ async onEnable(context) { const response = await httpClient.sendRequest({ method: HttpMethod.POST, - url: `https://app.formbricks.com/api/v1/webhooks`, + url: `${context.auth.appUrl}/api/v1/webhooks`, body: { url: context.webhookUrl, triggers: [eventType], @@ -99,7 +99,7 @@ export const formBricksRegisterTrigger = ({ if (webhook != null) { const request: HttpRequest = { method: HttpMethod.DELETE, - url: `https://app.formbricks.com/api/v1/webhooks/${webhook.webhookId}`, + url: `${context.auth.appUrl}/api/v1/webhooks/${webhook.webhookId}`, headers: { 'x-Api-Key': context.auth as unknown as string, }, diff --git a/packages/pieces/community/framework/package.json b/packages/pieces/community/framework/package.json index 5862be4300..33707d9ffc 100644 --- a/packages/pieces/community/framework/package.json +++ b/packages/pieces/community/framework/package.json @@ -1,5 +1,5 @@ { "name": "@activepieces/pieces-framework", - "version": "0.7.29", + "version": "0.7.30", "type": "commonjs" } \ No newline at end of file diff --git a/packages/pieces/community/framework/src/lib/property/index.ts b/packages/pieces/community/framework/src/lib/property/index.ts index e36478086f..6d6f1a0e62 100644 --- a/packages/pieces/community/framework/src/lib/property/index.ts +++ b/packages/pieces/community/framework/src/lib/property/index.ts @@ -2,12 +2,12 @@ import { InputProperty } from './input'; import { PieceAuthProperty } from './authentication'; import { Type } from '@sinclair/typebox'; -// EXPORTED +// EXPORTED export { ApFile } from './input/file-property'; export { DropdownProperty, MultiSelectDropdownProperty } from './input/dropdown/dropdown-prop'; export { DropdownState } from './input/dropdown/common'; -export { DynamicProperties } from './input/dynamic-prop'; +export { DynamicProperties, DynamicProp } from './input/dynamic-prop'; export { PropertyType } from './input/property-type'; export { Property } from './input'; export { PieceAuth } from './authentication'; diff --git a/packages/pieces/community/framework/src/lib/property/input/dynamic-prop.ts b/packages/pieces/community/framework/src/lib/property/input/dynamic-prop.ts index 95272c356e..e0794146ee 100644 --- a/packages/pieces/community/framework/src/lib/property/input/dynamic-prop.ts +++ b/packages/pieces/community/framework/src/lib/property/input/dynamic-prop.ts @@ -6,13 +6,13 @@ import { PropertyContext } from "../../context"; import { ValidationInputType } from "../../validators/types"; import { PropertyType } from "./property-type"; -const DynamicProp = Type.Union([ +export const DynamicProp = Type.Union([ ShortTextProperty, StaticDropdownProperty, StaticMultiSelectDropdownProperty, ]) -type DynamicProp = +export type DynamicProp = | ShortTextProperty | StaticDropdownProperty | StaticMultiSelectDropdownProperty; diff --git a/packages/pieces/community/framework/src/lib/property/input/text-property.ts b/packages/pieces/community/framework/src/lib/property/input/text-property.ts index 92ca95c84d..50bf2ef018 100644 --- a/packages/pieces/community/framework/src/lib/property/input/text-property.ts +++ b/packages/pieces/community/framework/src/lib/property/input/text-property.ts @@ -20,4 +20,4 @@ export const LongTextProperty = Type.Composite([ ]) export type LongTextProperty = BasePropertySchema & - TPropertyValue; \ No newline at end of file + TPropertyValue; diff --git a/packages/pieces/community/lever/package.json b/packages/pieces/community/lever/package.json index 06fb46b668..a481c79369 100644 --- a/packages/pieces/community/lever/package.json +++ b/packages/pieces/community/lever/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-lever", - "version": "0.0.1" + "version": "0.0.2" } diff --git a/packages/pieces/community/lever/src/index.ts b/packages/pieces/community/lever/src/index.ts index 637397ef71..f3c0da8ab8 100644 --- a/packages/pieces/community/lever/src/index.ts +++ b/packages/pieces/community/lever/src/index.ts @@ -3,6 +3,8 @@ import { createCustomApiCallAction } from '@activepieces/pieces-common'; import { getOpportunity } from './lib/actions/get-opportunity'; import { updateOpportunityStage } from './lib/actions/update-opportunity-stage'; import { listOpportunityForms } from './lib/actions/list-opportunity-forms'; +import { listOpportunityFeedback } from './lib/actions/list-opportunity-feedback'; +import { addFeedbackToOpportunity } from './lib/actions/add-feedback-to-opportunity'; export const LEVER_BASE_URL = 'https://api.lever.co/v1'; @@ -29,6 +31,8 @@ export const lever = createPiece({ getOpportunity, updateOpportunityStage, listOpportunityForms, + listOpportunityFeedback, + addFeedbackToOpportunity, createCustomApiCallAction({ baseUrl: () => { return LEVER_BASE_URL; diff --git a/packages/pieces/community/lever/src/lib/actions/add-feedback-to-opportunity.ts b/packages/pieces/community/lever/src/lib/actions/add-feedback-to-opportunity.ts new file mode 100644 index 0000000000..f56dd3788d --- /dev/null +++ b/packages/pieces/community/lever/src/lib/actions/add-feedback-to-opportunity.ts @@ -0,0 +1,288 @@ +import { + createAction, + DynamicPropsValue, + Property, +} from '@activepieces/pieces-framework'; +import { LEVER_BASE_URL, LeverAuth, leverAuth } from '../..'; +import { + AuthenticationType, + httpClient, + HttpMethod, +} from '@activepieces/pieces-common'; +import { LeverFieldMapping } from '../common'; + +export const addFeedbackToOpportunity = createAction({ + // auth: check https://www.activepieces.com/docs/developers/piece-reference/authentication, + name: 'addFeedbackToOpportunity', + displayName: 'Add feedback to opportunity', + description: 'Provide feedback to a candidate after an interview', + auth: leverAuth, + props: { + performAs: Property.Dropdown({ + displayName: 'Feedback author', + required: true, + refreshers: ['auth'], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Please connect first.', + options: [], + }; + } + + const users = []; + let cursor = undefined; + do { + const queryParams: Record = { + include: 'name', + }; + if (cursor) { + queryParams['offset'] = cursor; + } + + const response = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/users`, + queryParams: queryParams, + authentication: { + type: AuthenticationType.BASIC, + username: (auth as LeverAuth).apiKey, + password: '', + }, + }); + cursor = response.body.next; + const usersPage = response.body.data.map( + (user: { id: string; name: string }) => { + return { + label: user.name, + value: user.id, + }; + } + ); + users.push(...usersPage); + } while (cursor !== undefined); + + return { + options: users, + }; + }, + }), + opportunityId: Property.ShortText({ + displayName: 'Opportunity ID', + required: true, + }), + panelId: Property.Dropdown({ + displayName: 'Interview panel', + required: false, + refreshers: ['auth', 'opportunityId'], + options: async ({ auth, opportunityId }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Please connect first.', + options: [], + }; + } + if (!opportunityId) { + return { + disabled: true, + placeholder: 'Please select a candidate (opportunity).', + options: [], + }; + } + const response = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/opportunities/${opportunityId}/panels?expand=stage&include=id&include=stage&include=start`, + authentication: { + type: AuthenticationType.BASIC, + username: (auth as LeverAuth).apiKey, + password: '', + }, + }); + return { + options: response.body.data.map( + (panel: { id: string; start: number; stage: { text: string } }) => { + const interviewDate = new Date(panel.start); + return { + label: `${interviewDate.toLocaleDateString()} - ${ + panel.stage.text + }`, + value: panel.id, + }; + } + ), + }; + }, + }), + interviewId: Property.Dropdown({ + displayName: 'Interview', + required: false, + refreshers: ['auth', 'opportunityId', 'panelId'], + options: async ({ auth, opportunityId, panelId }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Please connect first.', + options: [], + }; + } + if (!opportunityId) { + return { + disabled: true, + placeholder: 'Please select a candidate (opportunity).', + options: [], + }; + } + if (!panelId) { + return { + disabled: true, + placeholder: 'Please select an interview panel.', + options: [], + }; + } + const response = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/opportunities/${opportunityId}/panels/${panelId}?include=interviews`, + authentication: { + type: AuthenticationType.BASIC, + username: (auth as LeverAuth).apiKey, + password: '', + }, + }); + return { + options: response.body.data.interviews.map( + (interview: { id: string; subject: string }) => { + return { label: interview.subject, value: interview.id }; + } + ), + }; + }, + }), + feedbackFields: Property.DynamicProperties({ + displayName: 'Fields', + required: true, + refreshers: ['auth', 'opportunityId', 'panelId', 'interviewId'], + props: async ({ auth, opportunityId, panelId, interviewId }) => { + if (!auth || !opportunityId || !panelId || !interviewId) { + return { + disabled: true, + placeholder: + 'Please connect your Lever account first and select an interview', + options: [], + }; + } + const fields: DynamicPropsValue = {}; + try { + const interviewResponse = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/opportunities/${opportunityId}/panels/${panelId}?include=interviews`, + authentication: { + type: AuthenticationType.BASIC, + username: (auth as LeverAuth).apiKey, + password: '', + }, + }); + const interview = interviewResponse.body.data.interviews.find( + (interview: { id: string }) => + interview.id === (interviewId as unknown as string) + ); + const feedbackTemplateResponse = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/feedback_templates/${interview.feedbackTemplate}`, + authentication: { + type: AuthenticationType.BASIC, + username: (auth as LeverAuth).apiKey, + password: '', + }, + }); + feedbackTemplateResponse.body.data.fields.map( + (field: { + id: string; + text: string; + description: string; + required: boolean; + type: string; + options?: { text: string; optionId: string }[]; + scores?: { text: string; description: string }[]; + }) => { + const mappedField = + LeverFieldMapping[field.type] || LeverFieldMapping['default']; + mappedField.buildActivepieceType(fields, field); + } + ); + } catch (e) { + console.error( + 'Unexpected error while building dynamic properties', + e + ); + } + return fields; + }, + }), + }, + async run({ auth, propsValue }) { + const interviewResponse = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/opportunities/${propsValue.opportunityId}/panels/${propsValue.panelId}?include=interviews`, + authentication: { + type: AuthenticationType.BASIC, + username: (auth as LeverAuth).apiKey, + password: '', + }, + }); + const interview = interviewResponse.body.data.interviews.find( + (interview: { id: string }) => + interview.id === (propsValue.interviewId as unknown as string) + ); + + const feedbackTemplateResponse = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/feedback_templates/${interview.feedbackTemplate}`, + authentication: { + type: AuthenticationType.BASIC, + username: (auth as LeverAuth).apiKey, + password: '', + }, + }); + + const templateFields = feedbackTemplateResponse.body.data.fields; + const groupedValues = Object.entries(propsValue.feedbackFields).reduce< + Record + >((values, [fieldId, fieldValue]: [string, DynamicPropsValue]) => { + const canonicalId = fieldId.substring(0, 36); + values[canonicalId] = values[canonicalId] ?? []; + values[canonicalId].push(fieldValue); + return values; + }, {}); + + const payload = { + baseTemplateId: interview.feedbackTemplate, + panel: propsValue.panelId, + interview: propsValue.interviewId, + fieldValues: Object.entries(groupedValues).map(([fieldId, values]) => { + const templateField = templateFields.find( + (tf: { id: string }) => tf.id === fieldId + ); + const mappedField = + templateField.type in LeverFieldMapping + ? LeverFieldMapping[templateField.type] + : LeverFieldMapping['default']; + return mappedField.buildLeverType(fieldId, values); + }), + }; + + const response = await httpClient.sendRequest({ + method: HttpMethod.POST, + url: `${LEVER_BASE_URL}/opportunities/${propsValue.opportunityId}/feedback?perform_as=${propsValue.performAs}`, + authentication: { + type: AuthenticationType.BASIC, + username: auth.apiKey, + password: '', + }, + body: payload, + }); + + return response.body.data; + }, +}); diff --git a/packages/pieces/community/lever/src/lib/actions/list-opportunity-feedback.ts b/packages/pieces/community/lever/src/lib/actions/list-opportunity-feedback.ts new file mode 100644 index 0000000000..e88a7cb308 --- /dev/null +++ b/packages/pieces/community/lever/src/lib/actions/list-opportunity-feedback.ts @@ -0,0 +1,70 @@ +import { createAction, Property } from '@activepieces/pieces-framework'; +import { LEVER_BASE_URL, LeverAuth, leverAuth } from '../..'; +import { + AuthenticationType, + httpClient, + HttpMethod, +} from '@activepieces/pieces-common'; + +export const listOpportunityFeedback = createAction({ + name: 'listOpportunityFeedback', + displayName: 'List opportunity feedback', + description: + 'Get all feedback for a given opportunity, optionally for a given template', + auth: leverAuth, + props: { + opportunityId: Property.ShortText({ + displayName: 'Opportunity ID', + required: true, + }), + template: Property.Dropdown({ + displayName: 'Feedback template', + required: false, + refreshers: ['auth'], + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Please connect first.', + options: [], + }; + } + const response = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/feedback_templates?include=text`, + authentication: { + type: AuthenticationType.BASIC, + username: (auth as LeverAuth).apiKey, + password: '', + }, + }); + return { + options: response.body.data.map( + (template: { text: string; id: string }) => { + return { label: template.text, value: template.id }; + } + ), + }; + }, + }), + }, + async run({ auth, propsValue }) { + const response = await httpClient.sendRequest({ + method: HttpMethod.GET, + url: `${LEVER_BASE_URL}/opportunities/${propsValue.opportunityId}/feedback`, + authentication: { + type: AuthenticationType.BASIC, + username: auth.apiKey, + password: '', + }, + }); + const feedback = response.body.data; + if (propsValue.template) { + return feedback.filter( + (form: { baseTemplateId: string }) => + form.baseTemplateId === propsValue.template + ); + } + return feedback; + }, +}); diff --git a/packages/pieces/community/lever/src/lib/actions/update-opportunity-stage.ts b/packages/pieces/community/lever/src/lib/actions/update-opportunity-stage.ts index 19037e029a..9bb1ba8ede 100644 --- a/packages/pieces/community/lever/src/lib/actions/update-opportunity-stage.ts +++ b/packages/pieces/community/lever/src/lib/actions/update-opportunity-stage.ts @@ -58,6 +58,7 @@ export const updateOpportunityStage = createAction({ }, body: { stage: propsValue.stage }, }); + return response.body.data; }, }); diff --git a/packages/pieces/community/lever/src/lib/common.ts b/packages/pieces/community/lever/src/lib/common.ts new file mode 100644 index 0000000000..7bf4d7d4a2 --- /dev/null +++ b/packages/pieces/community/lever/src/lib/common.ts @@ -0,0 +1,135 @@ +import { DynamicPropsValue, Property } from '@activepieces/pieces-framework'; + +export type LeverFieldType = { + id: string; + text: string; + description: string; + required: boolean; + type: string; + options?: { text: string; optionId: string }[]; + scores?: { text: string; description: string }[]; +}; + +export const LeverFieldMapping: Record< + string, + { + buildActivepieceType: ( + fields: DynamicPropsValue, + field: LeverFieldType + ) => void; + buildLeverType: ( + id: string, + propsValues: DynamicPropsValue[] + ) => { + id: string; + value: + | string + | string[] + | number + | number[] + | { score: number; comment?: string }[]; + }; + } +> = { + default: { + buildActivepieceType: (fields, field) => + (fields[field.id] = Property.ShortText({ + displayName: field.text, + description: field.description, + required: field.required, + })), + buildLeverType: (id, propsValues) => ({ + id, + value: propsValues[0] as unknown as string, + }), + }, + textarea: { + buildActivepieceType: (fields, field) => + (fields[field.id] = Property.LongText({ + displayName: field.text, + description: field.description, + required: field.required, + })), + buildLeverType: (id, propsValues) => ({ + id, + value: propsValues[0] as unknown as string, + }), + }, + 'text-no': { + buildActivepieceType: (fields, field) => + (fields[field.id] = Property.Checkbox({ + displayName: field.text, + description: field.description, + required: field.required, + })), + buildLeverType: (id, propsValues) => ({ + id, + value: propsValues[0] as unknown as string, + }), + }, + 'multiple-select': { + buildActivepieceType: (fields, field) => + (fields[field.id] = Property.StaticMultiSelectDropdown({ + displayName: field.text, + description: field.description, + required: field.required, + options: { + disabled: false, + options: + field.options?.map((option: { text: string; optionId: string }) => { + return { value: option.text, label: option.text }; + }) || [], + }, + })), + buildLeverType: (id, propsValues) => ({ + id, + value: propsValues[0] as unknown as string, + }), + }, + 'score-system': { + buildActivepieceType: (fields, field) => + (fields[field.id] = Property.StaticDropdown({ + displayName: field.text, + description: field.description, + required: field.required, + options: { + options: + field.options?.map((option) => { + return { value: option.text, label: option.text }; + }) || [], + }, + })), + buildLeverType: (id, propsValues) => ({ + id, + value: propsValues[0] as unknown as string, + }), + }, + scorecard: { + buildActivepieceType: (fields, field) => + field.scores?.map((score, index) => { + fields[`${field.id}-${index}`] = Property.StaticDropdown({ + displayName: score.text, + description: score.description, + required: field.required, + options: { + options: [ + { label: 'n.a.', value: 0 }, + { label: '๐Ÿ‘Ž๐Ÿ‘Ž', value: 1 }, + { label: '๐Ÿ‘Ž', value: 2 }, + { label: '๐Ÿ‘', value: 3 }, + { label: '๐Ÿ‘๐Ÿ‘', value: 4 }, + ], + }, + }); + }), + buildLeverType: (id, propsValues) => ({ + id, + value: propsValues.map((propsValue: DynamicPropsValue) => { + return { + score: propsValue as unknown as number, + comment: '', + }; + }), + }), + }, +}; diff --git a/packages/pieces/community/notion/package.json b/packages/pieces/community/notion/package.json index a55daadcf0..0fcf17e575 100644 --- a/packages/pieces/community/notion/package.json +++ b/packages/pieces/community/notion/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-notion", - "version": "0.2.12" + "version": "0.2.13" } \ No newline at end of file diff --git a/packages/pieces/community/notion/src/lib/triggers/updated-database-item.ts b/packages/pieces/community/notion/src/lib/triggers/updated-database-item.ts index ba86360b3b..72fff717f3 100644 --- a/packages/pieces/community/notion/src/lib/triggers/updated-database-item.ts +++ b/packages/pieces/community/notion/src/lib/triggers/updated-database-item.ts @@ -128,9 +128,9 @@ const polling: Polling< lastFetchEpochMS === 0 ? null : dayjs(lastFetchEpochMS).toISOString() ); return items.results.map((item) => { - const object = item as { created_time: string }; + const object = item as { last_edited_time: string }; return { - epochMilliSeconds: dayjs(object.created_time).valueOf(), + epochMilliSeconds: dayjs(object.last_edited_time).valueOf(), data: item, }; }); diff --git a/packages/pieces/community/openai/package.json b/packages/pieces/community/openai/package.json index b956d5a1f9..f4068e4900 100644 --- a/packages/pieces/community/openai/package.json +++ b/packages/pieces/community/openai/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-openai", - "version": "0.3.23" + "version": "0.3.24" } \ No newline at end of file diff --git a/packages/pieces/community/openai/src/lib/actions/extract-structure-data.action.ts b/packages/pieces/community/openai/src/lib/actions/extract-structure-data.action.ts index eddd8d59e0..4539d103f9 100644 --- a/packages/pieces/community/openai/src/lib/actions/extract-structure-data.action.ts +++ b/packages/pieces/community/openai/src/lib/actions/extract-structure-data.action.ts @@ -51,26 +51,20 @@ export const extractStructuredDataAction = createAction({ displayName: 'Unstructured Text', required: true, }), - prompt: Property.LongText({ - displayName: 'Prompt', - description: - 'Provide a brief description of what sort of data you want extracted from the unstructured text.', - required: true, - }), params: Property.Array({ - displayName: 'Structured Data Definition', + displayName: 'Data Definition', required: true, properties: { propName: Property.ShortText({ displayName: 'Name', description: - 'Provide the name of the values you want to extract from the unstructured text. Name should be unique and short. ', + 'Provide the name of the value you want to extract from the unstructured text. The name should be unique and short. ', required: true, }), propDescription: Property.LongText({ displayName: 'Description', description: - 'Brief description of the parameter, defining what data will be extracted to this parameter.', + 'Brief description of the data, this hints for the AI on what to look for', required: false, }), propDataType: Property.StaticDropdown({ @@ -88,29 +82,28 @@ export const extractStructuredDataAction = createAction({ }, }), propIsRequired: Property.Checkbox({ - displayName: 'Is Property Required?', - description: 'If the property must be present, the action will fail if it is not found.', + displayName: 'Fail if Not present?', required: true, - defaultValue: true, + defaultValue: false, }), }, }), }, async run(context) { - const { model, text, prompt } = context.propsValue; + const { model, text } = context.propsValue; const paramInputArray = context.propsValue.params as ParamInput[]; const functionParams: Record = {}; const requiredFunctionParams: string[] = []; for (const param of paramInputArray) { functionParams[param.propName] = { type: param.propDataType, - description: param.propDescription, + description: param.propDescription ?? param.propName, }; if (param.propIsRequired) { requiredFunctionParams.push(param.propName); } } - + const prompt = 'Extract the following data from the provided text' const openai = new OpenAI({ apiKey: context.auth, }); @@ -138,8 +131,8 @@ export const extractStructuredDataAction = createAction({ if (toolCallsResponse) { return JSON.parse(toolCallsResponse[0].function.arguments); } else { - throw Error(JSON.stringify({ - message: 'Unable to extract data. Please provide valid params and text.' + throw new Error(JSON.stringify({ + message: "OpenAI couldn't extract the fields from the above text." })); } }, diff --git a/packages/pieces/community/pdf/package.json b/packages/pieces/community/pdf/package.json index 6c38c0d693..a6f0e7b593 100644 --- a/packages/pieces/community/pdf/package.json +++ b/packages/pieces/community/pdf/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "dependencies": { "@activepieces/pieces-framework": "*", - "pdf-text-reader": "4.0.1", + "pdf-text-reader": "4.1.0", "tslib": "2.6.2" }, "main": "./src/index.js", diff --git a/packages/pieces/community/taskade/.eslintrc.json b/packages/pieces/community/taskade/.eslintrc.json new file mode 100644 index 0000000000..4a4e695c54 --- /dev/null +++ b/packages/pieces/community/taskade/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": [ + "../../../../.eslintrc.base.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/community/taskade/README.md b/packages/pieces/community/taskade/README.md new file mode 100644 index 0000000000..32ad3fcc9f --- /dev/null +++ b/packages/pieces/community/taskade/README.md @@ -0,0 +1,7 @@ +# pieces-taskade + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-taskade` to build the library. diff --git a/packages/pieces/community/taskade/package.json b/packages/pieces/community/taskade/package.json new file mode 100644 index 0000000000..f865af36f7 --- /dev/null +++ b/packages/pieces/community/taskade/package.json @@ -0,0 +1,4 @@ +{ + "name": "@activepieces/piece-taskade", + "version": "0.0.1" +} diff --git a/packages/pieces/community/taskade/project.json b/packages/pieces/community/taskade/project.json new file mode 100644 index 0000000000..f33717b16d --- /dev/null +++ b/packages/pieces/community/taskade/project.json @@ -0,0 +1,38 @@ +{ + "name": "pieces-taskade", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/community/taskade/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/community/taskade", + "tsConfig": "packages/pieces/community/taskade/tsconfig.lib.json", + "packageJson": "packages/pieces/community/taskade/package.json", + "main": "packages/pieces/community/taskade/src/index.ts", + "assets": [ + "packages/pieces/community/taskade/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-taskade {args.ver} {args.tag}", + "dependsOn": [ + "build" + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ] + } + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/pieces/community/taskade/src/index.ts b/packages/pieces/community/taskade/src/index.ts new file mode 100644 index 0000000000..32b2796930 --- /dev/null +++ b/packages/pieces/community/taskade/src/index.ts @@ -0,0 +1,35 @@ +import { createPiece, PieceAuth } from '@activepieces/pieces-framework'; +import { createTaskAction } from './lib/actions/create-task.action'; +import { PieceCategory } from '@activepieces/shared'; +import { completeTaskAction } from './lib/actions/complete-task.action'; +import { deleteTaskAction } from './lib/actions/delete-task.action'; +import { createCustomApiCallAction } from '@activepieces/pieces-common'; + +export const taskadeAuth = PieceAuth.SecretText({ + displayName: 'Personal Token', + required: true, + description: ` + 1. Navigate to https://taskade.com/settings/password and scroll down to Personal Access Tokens. + 2. Create your personal access token with any name.`, +}); + +export const taskade = createPiece({ + displayName: 'Taskade', + auth: taskadeAuth, + minimumSupportedRelease: '0.20.0', + categories: [PieceCategory.PRODUCTIVITY], + description: 'collaboration platform for remote teams to organize and manage projects', + logoUrl: 'https://cdn.activepieces.com/pieces/taskade.png', + authors: ['kishanprmr'], + actions: [ + createTaskAction, + completeTaskAction, + deleteTaskAction, + createCustomApiCallAction({ + baseUrl: () => 'https://www.taskade.com/api/v1', + auth: taskadeAuth, + authMapping: (auth) => ({ Authorization: `Bearer ${auth as string}` }), + }), + ], + triggers: [], +}); diff --git a/packages/pieces/community/taskade/src/lib/actions/complete-task.action.ts b/packages/pieces/community/taskade/src/lib/actions/complete-task.action.ts new file mode 100644 index 0000000000..e7155cd3ae --- /dev/null +++ b/packages/pieces/community/taskade/src/lib/actions/complete-task.action.ts @@ -0,0 +1,24 @@ +import { taskadeAuth } from '../../'; +import { createAction } from '@activepieces/pieces-framework'; +import { taskadeProps } from '../common/props'; +import { TaskadeAPIClient } from '../common/client'; + +export const completeTaskAction = createAction({ + auth: taskadeAuth, + name: 'taskade-complete-task', + displayName: 'Complete Task', + description: 'Complete a task in a project.', + props: { + workspace_id: taskadeProps.workspace_id, + folder_id: taskadeProps.folder_id, + project_id: taskadeProps.project_id, + task_id: taskadeProps.task_id, + }, + async run(context) { + const { project_id, task_id } = context.propsValue; + + const client = new TaskadeAPIClient(context.auth); + + return await client.completeTask(project_id, task_id); + }, +}); diff --git a/packages/pieces/community/taskade/src/lib/actions/create-task.action.ts b/packages/pieces/community/taskade/src/lib/actions/create-task.action.ts new file mode 100644 index 0000000000..6f9fe53b46 --- /dev/null +++ b/packages/pieces/community/taskade/src/lib/actions/create-task.action.ts @@ -0,0 +1,68 @@ +import { taskadeAuth } from '../../'; +import { createAction, Property } from '@activepieces/pieces-framework'; +import { taskadeProps } from '../common/props'; +import { TaskadeAPIClient } from '../common/client'; + +export const createTaskAction = createAction({ + auth: taskadeAuth, + name: 'taskade-create-task', + displayName: 'Create Task', + description: 'Creates a new task.', + props: { + workspace_id: taskadeProps.workspace_id, + folder_id: taskadeProps.folder_id, + project_id: taskadeProps.project_id, + content_type: Property.StaticDropdown({ + displayName: 'Content Type', + required: true, + defaultValue: 'text/markdown', + options: { + disabled: false, + options: [ + { + label: 'text/markdown', + value: 'text/markdown', + }, + { + label: 'text/plain', + value: 'text/plain', + }, + ], + }, + }), + content: Property.LongText({ + displayName: 'Task Content', + required: true, + }), + placement: Property.StaticDropdown({ + displayName: 'Placement', + description: 'Placement of task in block', + required: true, + defaultValue: 'afterbegin', + options: { + disabled: false, + options: [ + { + label: 'afterbegin', + value: 'afterbegin', + }, + { + label: 'beforeend', + value: 'beforeend', + }, + ], + }, + }), + }, + async run(context) { + const { project_id, content_type, content, placement } = context.propsValue; + + const client = new TaskadeAPIClient(context.auth); + + return await client.createTask(project_id, { + content, + contentType: content_type, + placement, + }); + }, +}); diff --git a/packages/pieces/community/taskade/src/lib/actions/delete-task.action.ts b/packages/pieces/community/taskade/src/lib/actions/delete-task.action.ts new file mode 100644 index 0000000000..82361f79bb --- /dev/null +++ b/packages/pieces/community/taskade/src/lib/actions/delete-task.action.ts @@ -0,0 +1,24 @@ +import { taskadeAuth } from '../../'; +import { createAction } from '@activepieces/pieces-framework'; +import { taskadeProps } from '../common/props'; +import { TaskadeAPIClient } from '../common/client'; + +export const deleteTaskAction = createAction({ + auth: taskadeAuth, + name: 'taskade-delete-task', + displayName: 'Delete Task', + description: 'Delete an existing task in a project.', + props: { + workspace_id: taskadeProps.workspace_id, + folder_id: taskadeProps.folder_id, + project_id: taskadeProps.project_id, + task_id: taskadeProps.task_id, + }, + async run(context) { + const { project_id, task_id } = context.propsValue; + + const client = new TaskadeAPIClient(context.auth); + + return await client.deleteTask(project_id, task_id); + }, +}); diff --git a/packages/pieces/community/taskade/src/lib/common/client.ts b/packages/pieces/community/taskade/src/lib/common/client.ts new file mode 100644 index 0000000000..9cdeff7b62 --- /dev/null +++ b/packages/pieces/community/taskade/src/lib/common/client.ts @@ -0,0 +1,105 @@ +import { + HttpMessageBody, + HttpMethod, + QueryParams, + httpClient, + HttpRequest, + AuthenticationType, +} from '@activepieces/pieces-common'; +import { + CreateTaskDateParams, + CreateTaskParams, + ListAPIResponse, + ProjectResponse, + CreateTaskResponse, + WorkspaceFolderResponse, + WorkspaceResponse, + TaskResponse, +} from './types'; + +type RequestParams = Record; + +export class TaskadeAPIClient { + constructor(private personalToken: string) {} + + async makeRequest( + method: HttpMethod, + resourceUri: string, + query?: RequestParams, + body: any | undefined = undefined, + ): Promise { + const baseUrl = 'https://www.taskade.com/api/v1'; + const qs: QueryParams = {}; + + if (query) { + for (const [key, value] of Object.entries(query)) { + if (value !== null && value !== undefined) { + qs[key] = String(value); + } + } + } + + const request: HttpRequest = { + method, + url: baseUrl + resourceUri, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: this.personalToken, + }, + queryParams: qs, + body, + }; + + const response = await httpClient.sendRequest(request); + return response.body; + } + + async listWorkspaces(): Promise> { + return await this.makeRequest(HttpMethod.GET, '/workspaces'); + } + + async listWorkspaceFolders( + workspace_id: string, + ): Promise> { + return await this.makeRequest(HttpMethod.GET, `/workspaces/${workspace_id}/folders`); + } + + async listProjects(folder_id: string): Promise> { + return await this.makeRequest(HttpMethod.GET, `/folders/${folder_id}/projects`); + } + + async createTask(projectId: string, params: CreateTaskParams): Promise { + return await this.makeRequest(HttpMethod.POST, `/projects/${projectId}/tasks`, undefined, { + tasks: [params], + }); + } + + async createTaskDate(projectId: string, taskId: string, params: CreateTaskDateParams) { + return await this.makeRequest( + HttpMethod.PUT, + `/projects/${projectId}/tasks/${taskId}/date`, + undefined, + params, + ); + } + + async listTasks( + projectId: string, + params: RequestParams, + ): Promise> { + return await this.makeRequest(HttpMethod.GET, `/projects/${projectId}/tasks`, params); + } + + async completeTask(projectId: string, taskId: string) { + return await this.makeRequest( + HttpMethod.POST, + `/projects/${projectId}/tasks/${taskId}/complete`, + undefined, + {}, + ); + } + + async deleteTask(projectId: string, taskId: string) { + return await this.makeRequest(HttpMethod.DELETE, `/projects/${projectId}/tasks/${taskId}`); + } +} diff --git a/packages/pieces/community/taskade/src/lib/common/props.ts b/packages/pieces/community/taskade/src/lib/common/props.ts new file mode 100644 index 0000000000..25bb75d09f --- /dev/null +++ b/packages/pieces/community/taskade/src/lib/common/props.ts @@ -0,0 +1,128 @@ +import { DropdownOption, Property } from '@activepieces/pieces-framework'; +import { TaskadeAPIClient } from './client'; + +const createEmptyOptions = (placeholder: string) => { + return { + disabled: true, + options: [], + placeholder, + }; +}; + +export const taskadeProps = { + workspace_id: Property.Dropdown({ + displayName: 'Workspace', + refreshers: [], + required: true, + options: async ({ auth }) => { + if (!auth) { + return createEmptyOptions('Please connect account first.'); + } + + const client = new TaskadeAPIClient(auth as string); + const response = await client.listWorkspaces(); + + const options: DropdownOption[] = []; + + for (const workspace of response.items) { + options.push({ label: workspace.name, value: workspace.id }); + } + + return { + disabled: false, + options, + }; + }, + }), + folder_id: Property.Dropdown({ + displayName: 'Folder', + refreshers: ['workspace_id'], + required: false, + options: async ({ auth, workspace_id }) => { + if (!auth) { + return createEmptyOptions('Please connect account first.'); + } + if (!workspace_id) { + return createEmptyOptions('Please select workspace.'); + } + + const client = new TaskadeAPIClient(auth as string); + const response = await client.listWorkspaceFolders(workspace_id as string); + + const options: DropdownOption[] = []; + + for (const folder of response.items) { + options.push({ label: folder.name, value: folder.id }); + } + + return { + disabled: false, + options, + }; + }, + }), + project_id: Property.Dropdown({ + displayName: 'Project', + refreshers: ['workspace_id', 'folder_id'], + required: true, + options: async ({ auth, workspace_id, folder_id }) => { + if (!auth) { + return createEmptyOptions('Please connect account first.'); + } + if (!workspace_id) { + return createEmptyOptions('Please select workspace.'); + } + + const workspaceId = workspace_id as string; + const folderId = (folder_id as string) ?? workspaceId; + + const client = new TaskadeAPIClient(auth as string); + const response = await client.listProjects(folderId as string); + + const options: DropdownOption[] = []; + + for (const project of response.items) { + options.push({ label: project.name, value: project.id }); + } + + return { + disabled: false, + options, + }; + }, + }), + task_id: Property.Dropdown({ + displayName: 'Task', + refreshers: ['project_id'], + required: true, + options: async ({ auth, project_id }) => { + if (!auth) { + return createEmptyOptions('Please connect account first.'); + } + if (!project_id) { + return createEmptyOptions('Please select project.'); + } + + const client = new TaskadeAPIClient(auth as string); + const options: DropdownOption[] = []; + + let after; + let moreTasks = true; + while (moreTasks) { + const response = await client.listTasks(project_id as string, { limit: 100, after }); + if (response.items.length === 0) { + moreTasks = false; + } else { + after = response.items[response.items.length - 1].id; + for (const task of response.items) { + options.push({ label: task.text, value: task.id }); + } + } + } + return { + disabled: false, + options, + }; + }, + }), +}; diff --git a/packages/pieces/community/taskade/src/lib/common/types.ts b/packages/pieces/community/taskade/src/lib/common/types.ts new file mode 100644 index 0000000000..23463504a0 --- /dev/null +++ b/packages/pieces/community/taskade/src/lib/common/types.ts @@ -0,0 +1,42 @@ +export interface ListAPIResponse { + ok: boolean; + items: Array; +} + +export interface BaseResponse { + id: string; + name: string; +} + +export type WorkspaceResponse = BaseResponse; +export type WorkspaceFolderResponse = BaseResponse; +export type ProjectResponse = BaseResponse; + +export interface CreateTaskParams { + contentType: string; + content: string; + placement: string; +} + +export interface TaskResponse { + id: string; + parentId: string; + text: string; + completed: boolean; +} + +export interface CreateTaskResponse { + ok: boolean; + item: TaskResponse[]; +} + +export interface CreateTaskDateParams { + start: { + date: string; + time: string; + }; + end?: { + date: string; + time: string; + }; +} diff --git a/packages/pieces/community/taskade/tsconfig.json b/packages/pieces/community/taskade/tsconfig.json new file mode 100644 index 0000000000..059cd81661 --- /dev/null +++ b/packages/pieces/community/taskade/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/community/taskade/tsconfig.lib.json b/packages/pieces/community/taskade/tsconfig.lib.json new file mode 100644 index 0000000000..28369ef762 --- /dev/null +++ b/packages/pieces/community/taskade/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/community/webhook/package.json b/packages/pieces/community/webhook/package.json index 2cb8dfae35..63f73a93d1 100644 --- a/packages/pieces/community/webhook/package.json +++ b/packages/pieces/community/webhook/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-webhook", - "version": "0.1.2" -} \ No newline at end of file + "version": "0.1.3" +} diff --git a/packages/pieces/community/webhook/src/index.ts b/packages/pieces/community/webhook/src/index.ts index cfc6e926d7..3a62c1952a 100644 --- a/packages/pieces/community/webhook/src/index.ts +++ b/packages/pieces/community/webhook/src/index.ts @@ -7,7 +7,7 @@ export const webhook = createPiece({ description: 'Receive HTTP requests and trigger flows using unique URLs.', auth: PieceAuth.None(), categories: [PieceCategory.CORE], - minimumSupportedRelease: '0.22.0', + minimumSupportedRelease: '0.26.0', logoUrl: 'https://cdn.activepieces.com/pieces/webhook.svg', authors: ['abuaboud', 'pfernandez98'], actions: [], diff --git a/packages/pieces/community/webhook/src/lib/triggers/catch-hook.ts b/packages/pieces/community/webhook/src/lib/triggers/catch-hook.ts index 4c413d1a38..a4a4128d0c 100644 --- a/packages/pieces/community/webhook/src/lib/triggers/catch-hook.ts +++ b/packages/pieces/community/webhook/src/lib/triggers/catch-hook.ts @@ -7,17 +7,21 @@ import { import { assertNotNullOrUndefined } from '@activepieces/shared'; const message = ` -URL: +**Production URL:** \`\`\`text {{webhookUrl}} \`\`\` -If you are expecting a reply from this webhook, append **/sync** to the URL. - -In that case, you will also have to add an HTTP step with **return response** at the end of your flow. +**Testing URL:** +\`\`\`text +{{webhookUrl}}/test +\`\`\` +***Use this URL for testing the webhook and saving sample data. It won't start the flow***. -If the flow takes more than **30 seconds**, it will give a **408 Request Timeout** response. +**Notes:** +- If you are expecting a reply from this webhook, append **/sync** to the URL in that case, you will also have to add an HTTP step with **return response** at the end of your flow. +- If the flow takes more than **30 seconds**, it will give a **408 Request Timeout** response. `; enum AuthType { diff --git a/packages/pieces/community/whatsapp/.eslintrc.json b/packages/pieces/community/whatsapp/.eslintrc.json new file mode 100644 index 0000000000..4a4e695c54 --- /dev/null +++ b/packages/pieces/community/whatsapp/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": [ + "../../../../.eslintrc.base.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/community/whatsapp/README.md b/packages/pieces/community/whatsapp/README.md new file mode 100644 index 0000000000..85a4a45abf --- /dev/null +++ b/packages/pieces/community/whatsapp/README.md @@ -0,0 +1,7 @@ +# pieces-whatsapp + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-whatsapp` to build the library. diff --git a/packages/pieces/community/whatsapp/package.json b/packages/pieces/community/whatsapp/package.json new file mode 100644 index 0000000000..7ddc589990 --- /dev/null +++ b/packages/pieces/community/whatsapp/package.json @@ -0,0 +1,4 @@ +{ + "name": "@activepieces/piece-whatsapp", + "version": "0.0.1" +} diff --git a/packages/pieces/community/whatsapp/project.json b/packages/pieces/community/whatsapp/project.json new file mode 100644 index 0000000000..adad8436e1 --- /dev/null +++ b/packages/pieces/community/whatsapp/project.json @@ -0,0 +1,38 @@ +{ + "name": "pieces-whatsapp", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/community/whatsapp/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/community/whatsapp", + "tsConfig": "packages/pieces/community/whatsapp/tsconfig.lib.json", + "packageJson": "packages/pieces/community/whatsapp/package.json", + "main": "packages/pieces/community/whatsapp/src/index.ts", + "assets": [ + "packages/pieces/community/whatsapp/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-whatsapp {args.ver} {args.tag}", + "dependsOn": [ + "build" + ] + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ] + } + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/pieces/community/whatsapp/src/index.ts b/packages/pieces/community/whatsapp/src/index.ts new file mode 100644 index 0000000000..a856dea7e3 --- /dev/null +++ b/packages/pieces/community/whatsapp/src/index.ts @@ -0,0 +1,47 @@ + +import { createPiece, PieceAuth, Property } from "@activepieces/pieces-framework"; +import { sendMessage } from "./lib/actions/send-message"; +import { sendMedia } from "./lib/actions/send-media"; + +const markdown = ` +To Obtain a Phone Number ID and a Permanent System User Access Token, follow these steps: + +1. Go to https://developers.facebook.com/ +2. Make a new app, Select Other for usecase. +3. Choose Business as the type of app. +4. Add new Product -> WhatsApp. +5. Navigate to WhatsApp Settings > API Setup. +6. Select a phone number and copy its Phone number ID. +7. Login to your [Meta Business Manager](https://business.facebook.com/). +8. Click on Settings. +9. Create a new System User with access over the app and copy the access token. +`; + +export const whatsappAuth = PieceAuth.CustomAuth({ + required: true, + description: markdown, + props: { + access_token: PieceAuth.SecretText({ + displayName: 'System User Access Token', + description: 'The system user access token of your WhatsApp business account', + required: true, + }), + phoneNumberId: Property.ShortText({ + displayName: 'Phone Number ID', + description: 'The phone number ID of your WhatsApp business account', + required: true, + }), + }, +}); + + +export const whatsapp = createPiece({ + displayName: "WhatsApp Business", + description: 'Manage your WhatsApp business account', + auth: whatsappAuth, + minimumSupportedRelease: '0.20.0', + logoUrl: "https://cdn.activepieces.com/pieces/whatsapp.png", + authors: ["LevwTech"], + actions: [sendMessage, sendMedia], + triggers: [], +}); diff --git a/packages/pieces/community/whatsapp/src/lib/actions/send-media.ts b/packages/pieces/community/whatsapp/src/lib/actions/send-media.ts new file mode 100644 index 0000000000..a200ccfacf --- /dev/null +++ b/packages/pieces/community/whatsapp/src/lib/actions/send-media.ts @@ -0,0 +1,70 @@ +import { createAction, Property } from '@activepieces/pieces-framework'; +import { httpClient, HttpMethod } from '@activepieces/pieces-common'; +import { whatsappAuth } from '../..'; +import { supportedMediaTypes, capitalizeFirstLetter, mediaTypeSupportsCaption } from '../common/utils'; + +export const sendMedia = createAction({ + auth: whatsappAuth, + name: 'sendMedia', + displayName: 'Send Media', + description: 'Send a media message through WhatsApp', + props: { + to: Property.ShortText({ + displayName: 'To', + description: 'The recipient of the message', + required: true, + }), + type: Property.Dropdown({ + displayName: 'Type', + description: 'The type of media to send', + required: true, + options: async () => { + return { + options: supportedMediaTypes.map((type) => ({ + label: capitalizeFirstLetter(type), + value: type, + })), + }; + }, + refreshers: [] + }), + media: Property.ShortText({ + displayName: 'Media URL', + description: 'The URL of the media to send', + required: true, + }), + caption: Property.LongText({ + displayName: 'Caption', + description: 'A caption for the media', + required: false, + }), + filename: Property.LongText({ + displayName: 'Filename', + description: 'Filename of the document to send', + required: false, + }), + }, + async run(context) { + const { to, caption, media, type, filename } = context.propsValue; + const { access_token, phoneNumberId } = context.auth; + const body = { + messaging_product: "whatsapp", + recipient_type: "individual", + to, + type, + [type]: { + link: media, + } + } + if (caption && mediaTypeSupportsCaption(type)) (body[type] as any).caption = caption; + if (filename && type === 'document') (body[type] as any).filename = filename; + return await httpClient.sendRequest({ + method: HttpMethod.POST, + url: `https://graph.facebook.com/v17.0/${phoneNumberId}/messages`, + headers: { + Authorization: 'Bearer ' + access_token, + }, + body, + }); + }, +}); \ No newline at end of file diff --git a/packages/pieces/community/whatsapp/src/lib/actions/send-message.ts b/packages/pieces/community/whatsapp/src/lib/actions/send-message.ts new file mode 100644 index 0000000000..2aba8ac46a --- /dev/null +++ b/packages/pieces/community/whatsapp/src/lib/actions/send-message.ts @@ -0,0 +1,43 @@ +import { createAction, Property } from '@activepieces/pieces-framework'; +import { httpClient, HttpMethod } from '@activepieces/pieces-common'; +import { whatsappAuth } from '../..'; + + +export const sendMessage = createAction({ + auth: whatsappAuth, + name: 'sendMessage', + displayName: 'Send Message', + description: 'Send a text message through WhatsApp', + props: { + to: Property.ShortText({ + displayName: 'To', + description: 'The recipient of the message', + required: true, + }), + text: Property.LongText({ + displayName: 'Message', + description: 'The message to send', + required: true, + }), + }, + async run(context) { + const { to, text } = context.propsValue; + const { access_token, phoneNumberId } = context.auth; + return await httpClient.sendRequest({ + method: HttpMethod.POST, + url: `https://graph.facebook.com/v17.0/${phoneNumberId}/messages`, + headers: { + Authorization: 'Bearer ' + access_token, + }, + body: { + messaging_product: "whatsapp", + recipient_type: "individual", + to, + type: "text", + text: { + body: text + } + } + }); + }, +}); diff --git a/packages/pieces/community/whatsapp/src/lib/common/utils.ts b/packages/pieces/community/whatsapp/src/lib/common/utils.ts new file mode 100644 index 0000000000..1e327b8d8a --- /dev/null +++ b/packages/pieces/community/whatsapp/src/lib/common/utils.ts @@ -0,0 +1,3 @@ +export const supportedMediaTypes = ['image', 'audio', 'document', 'sticker', 'video']; +export const capitalizeFirstLetter = (word: string) => word.charAt(0).toUpperCase() + word.slice(1); +export const mediaTypeSupportsCaption = (type: string) => ['image', 'video', 'document'].includes(type); \ No newline at end of file diff --git a/packages/pieces/community/whatsapp/tsconfig.json b/packages/pieces/community/whatsapp/tsconfig.json new file mode 100644 index 0000000000..059cd81661 --- /dev/null +++ b/packages/pieces/community/whatsapp/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/community/whatsapp/tsconfig.lib.json b/packages/pieces/community/whatsapp/tsconfig.lib.json new file mode 100644 index 0000000000..0fe2205530 --- /dev/null +++ b/packages/pieces/community/whatsapp/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", "src/lib/actions/common/utils.ts"] +} diff --git a/packages/server/api/src/app/app-event-routing/app-event-routing.module.ts b/packages/server/api/src/app/app-event-routing/app-event-routing.module.ts index fc968cd194..3b50004bac 100644 --- a/packages/server/api/src/app/app-event-routing/app-event-routing.module.ts +++ b/packages/server/api/src/app/app-event-routing/app-event-routing.module.ts @@ -9,7 +9,8 @@ import { slack } from '@activepieces/piece-slack' import { square } from '@activepieces/piece-square' import { Piece } from '@activepieces/pieces-framework' import { logger, rejectedPromiseHandler } from '@activepieces/server-shared' -import { ActivepiecesError, ALL_PRINCIPAL_TYPES, +import { + ActivepiecesError, ALL_PRINCIPAL_TYPES, ErrorCode, EventPayload, isNil, diff --git a/packages/server/api/src/app/core/security/authz/project-authz-handler.ts b/packages/server/api/src/app/core/security/authz/project-authz-handler.ts index d4cb817995..a351d13b0f 100644 --- a/packages/server/api/src/app/core/security/authz/project-authz-handler.ts +++ b/packages/server/api/src/app/core/security/authz/project-authz-handler.ts @@ -11,7 +11,7 @@ export class ProjectAuthzHandler extends BaseSecurityHandler { '/v1/users/projects/:projectId/token', '/v1/webhooks', '/v1/webhooks/:flowId', - '/v1/webhooks/:flowId/simulate', + '/v1/webhooks/:flowId/test', '/v1/webhooks/:flowId/sync', ] diff --git a/packages/server/api/src/app/flows/flow-run/flow-run-service.ts b/packages/server/api/src/app/flows/flow-run/flow-run-service.ts index 0282e52894..a60db820c0 100644 --- a/packages/server/api/src/app/flows/flow-run/flow-run-service.ts +++ b/packages/server/api/src/app/flows/flow-run/flow-run-service.ts @@ -78,6 +78,31 @@ async function updateFlowRunToLatestFlowVersionId( }) } +function returnHandlerId(pauseMetadata: PauseMetadata | undefined, requestId: string | undefined): string { + const handlerId = engineResponseWatcher.getHandlerId() + if (isNil(pauseMetadata)) { + return handlerId + } + + if (pauseMetadata.type === PauseType.WEBHOOK && requestId === pauseMetadata.requestId && pauseMetadata.handlerId) { + return pauseMetadata.handlerId + } + else { + return handlerId + } +} + +function modifyPauseMetadata(pauseMetadata: PauseMetadata): PauseMetadata { + if (pauseMetadata.type === PauseType.WEBHOOK) { + return { + ...pauseMetadata, + handlerId: engineResponseWatcher.getHandlerId(), + } + } + + return pauseMetadata +} + export const flowRunService = { async list({ projectId, @@ -173,6 +198,8 @@ export const flowRunService = { flowRunId: flowRunToResume.id, projectId: flowRunToResume.projectId, flowVersionId: flowRunToResume.flowVersionId, + synchronousHandlerId: returnHandlerId(pauseMetadata, requestId), + hookType: HookType.AFTER_LOG, executionType, environment: RunEnvironment.PRODUCTION, }) @@ -292,7 +319,7 @@ export const flowRunService = { status: FlowRunStatus.PAUSED, logsFileId: logFileId, // eslint-disable-next-line @typescript-eslint/no-explicit-any - pauseMetadata: pauseMetadata as any, + pauseMetadata: modifyPauseMetadata(pauseMetadata) as any, }) const flowRun = await flowRunRepo.findOneByOrFail({ id: flowRunId }) diff --git a/packages/server/api/src/app/flows/flow.module.ts b/packages/server/api/src/app/flows/flow.module.ts index 6cfb78e397..88ec6c480e 100644 --- a/packages/server/api/src/app/flows/flow.module.ts +++ b/packages/server/api/src/app/flows/flow.module.ts @@ -10,7 +10,7 @@ import { folderController } from './folder/folder.controller' import { stepRunService } from './step-run/step-run-service' import { testTriggerController } from './test-trigger/test-trigger-controller' import { logger } from '@activepieces/server-shared' -import { CreateStepRunRequestBody, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' +import { CreateStepRunRequestBody, isFlowStateTerminal, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' export const flowModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(flowWorkerController, { prefix: '/v1/worker/flows' }) @@ -25,9 +25,21 @@ export const flowModule: FastifyPluginAsyncTypebox = async (app) => { projectId: principal.projectId, flowVersionId: data.flowVersionId, }) + socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_STARTED, flowRun) - await engineResponseWatcher.listen(flowRun.id, false) - socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_FINISHED, flowRun) + const eventEmitter = engineResponseWatcher.listen(flowRun.id) + eventEmitter.on(async (data) => { + const flowRun = await flowRunService.getOneOrThrow({ + id: data.requestId, + projectId: principal.projectId, + }) + + if (isFlowStateTerminal(flowRun.status)) { + engineResponseWatcher.removeListener(flowRun.id) + } + socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS, flowRun) + }) + } }) websocketService.addListener(WebsocketServerEvent.TEST_STEP_RUN, (socket) => { diff --git a/packages/server/api/src/app/webhooks/webhook-controller.ts b/packages/server/api/src/app/webhooks/webhook-controller.ts index 228d377fa0..7abe7f1041 100644 --- a/packages/server/api/src/app/webhooks/webhook-controller.ts +++ b/packages/server/api/src/app/webhooks/webhook-controller.ts @@ -65,7 +65,7 @@ export const webhookController: FastifyPluginAsyncTypebox = async (app) => { .send(response.body) }) - app.all('/:flowId/simulate', WEBHOOK_PARAMS, async (request, reply) => { + app.all('/:flowId/test', WEBHOOK_PARAMS, async (request, reply) => { const response = await handleWebhook({ request, flowId: request.params.flowId, @@ -104,7 +104,7 @@ async function handleWebhook({ request, flowId, async, simulate }: { request: Fa headers: {}, } } - return engineResponseWatcher.listen(requestId, true) + return engineResponseWatcher.oneTimeListener(requestId, true) } async function convertRequest(request: FastifyRequest): Promise { diff --git a/packages/server/api/src/app/webhooks/webhook-service.ts b/packages/server/api/src/app/webhooks/webhook-service.ts index 8c28f79133..e9e52342c5 100644 --- a/packages/server/api/src/app/webhooks/webhook-service.ts +++ b/packages/server/api/src/app/webhooks/webhook-service.ts @@ -1,4 +1,3 @@ -import { flowService } from '../flows/flow/flow.service' import { flowRunService, HookType } from '../flows/flow-run/flow-run-service' import { flowVersionService } from '../flows/flow-version/flow-version.service' import { triggerHooks } from '../flows/trigger' @@ -66,32 +65,16 @@ export const webhookService = { const { projectId } = flow - if (isNil(flow.publishedVersionId)) { + if (flow.status !== FlowStatus.ENABLED || isNil(flow.publishedVersionId)) { logger.info( - `[WebhookService#callback] flowInstance not found, flowId=${flow.id}`, + `[WebhookService#callback] flowInstance not found or not enabled ignoring the webhook, flowId=${flow.id}`, ) - const flowVersion = ( - await flowService.getOnePopulatedOrThrow({ - projectId, + throw new ActivepiecesError({ + code: ErrorCode.FLOW_NOT_FOUND, + params: { id: flow.id, - }) - ).version - const payloads: unknown[] = await triggerHooks.executeTrigger({ - projectId, - flowVersion, - payload, - simulate: false, + }, }) - payloads.forEach((resultPayload) => { - saveSampleDataForWebhookTesting(flow, resultPayload) - }) - return [] - } - if (flow.status !== FlowStatus.ENABLED) { - logger.info( - `[WebhookService#callback] flowInstance not found or not enabled ignoring the webhook, flowId=${flow.id}`, - ) - return [] } const flowVersion = await flowVersionService.getOneOrThrow( @@ -104,20 +87,20 @@ export const webhookService = { simulate: false, }) - payloads.forEach((payload) => { - triggerEventService - .saveEvent({ - flowId: flow.id, - payload, - projectId, - }) - .catch((e) => - logger.error( - e, - '[WebhookService#callback] triggerEventService.saveEvent', - ), - ) - }) + const savePayloads = payloads.map((payload) => + triggerEventService.saveEvent({ + flowId: flow.id, + payload, + projectId, + }).catch((e) => + logger.error( + e, + '[WebhookService#callback] triggerEventService.saveEvent', + ), + ), + ) + + await Promise.all(savePayloads) const filterPayloads = await dedupeService.filterUniquePayloads( flowVersion.id, @@ -180,7 +163,7 @@ export const webhookService = { flowId, simulate, }: GetWebhookUrlParams): Promise { - const suffix: WebhookUrlSuffix = simulate ? '/simulate' : '' + const suffix: WebhookUrlSuffix = simulate ? '/test' : '' const webhookPrefix = await this.getWebhookPrefix() return `${webhookPrefix}/${flowId}${suffix}` }, @@ -211,22 +194,7 @@ const getLatestFlowVersionOrThrow = async ( return flowVersion } -function saveSampleDataForWebhookTesting(flow: Flow, payload: unknown): void { - triggerEventService - .saveEvent({ - flowId: flow.id, - payload, - projectId: flow.projectId, - }) - .catch((e) => - logger.error( - e, - '[WebhookService#saveSampleDataForWebhookTesting] triggerEventService.saveEvent', - ), - ) -} - -type WebhookUrlSuffix = '' | '/simulate' +type WebhookUrlSuffix = '' | '/test' type GetWebhookUrlParams = { flowId: FlowId diff --git a/packages/server/api/src/app/webhooks/webhook-simulation/webhook-simulation-service.ts b/packages/server/api/src/app/webhooks/webhook-simulation/webhook-simulation-service.ts index 4e4dc92e83..61dfd76872 100644 --- a/packages/server/api/src/app/webhooks/webhook-simulation/webhook-simulation-service.ts +++ b/packages/server/api/src/app/webhooks/webhook-simulation/webhook-simulation-service.ts @@ -49,7 +49,7 @@ export const webhookSimulationService = { }) try { - const webhookSimulationExists = await webhookSimulationRepo.exist({ + const webhookSimulationExists = await webhookSimulationRepo.exists({ where: { flowId }, }) diff --git a/packages/server/api/src/app/workers/flow-worker/consumer/webook-consumer.ts b/packages/server/api/src/app/workers/flow-worker/consumer/webook-consumer.ts index 96eddd9757..36b54a574c 100644 --- a/packages/server/api/src/app/workers/flow-worker/consumer/webook-consumer.ts +++ b/packages/server/api/src/app/workers/flow-worker/consumer/webook-consumer.ts @@ -3,7 +3,7 @@ import { flowService } from '../../../flows/flow/flow.service' import { webhookService } from '../../../webhooks/webhook-service' import { EngineHttpResponse, engineResponseWatcher } from '../engine-response-watcher' import { WebhookJobData } from '../job-data' -import { isNil } from '@activepieces/shared' +import { FlowStatus, isNil } from '@activepieces/shared' export const webhookConsumer = { async consumeWebhook(data: WebhookJobData): Promise { @@ -17,6 +17,14 @@ export const webhookConsumer = { }) return } + if (flow.status !== FlowStatus.ENABLED && !simulate) { + await stopAndReply(data, { + status: StatusCodes.NOT_FOUND, + body: {}, + headers: {}, + }) + return + } const handshakeResponse = await webhookService.handshake({ flow, payload, @@ -51,7 +59,7 @@ export const webhookConsumer = { return } const firstRun = runs[0] - const response = await engineResponseWatcher.listen(firstRun.id, true) + const response = await engineResponseWatcher.oneTimeListener(firstRun.id, true) await stopAndReply(data, response) }, diff --git a/packages/server/api/src/app/workers/flow-worker/engine-response-watcher.ts b/packages/server/api/src/app/workers/flow-worker/engine-response-watcher.ts index aae19a1b6e..2975cc1afb 100644 --- a/packages/server/api/src/app/workers/flow-worker/engine-response-watcher.ts +++ b/packages/server/api/src/app/workers/flow-worker/engine-response-watcher.ts @@ -1,10 +1,10 @@ import { logger } from '@sentry/utils' import { StatusCodes } from 'http-status-codes' import { pubSub } from '../../helper/pubsub' -import { system, SystemProp } from '@activepieces/server-shared' +import { system, SystemProp, TypedEventEmitter } from '@activepieces/server-shared' import { apId } from '@activepieces/shared' -const listeners = new Map void>() +const listeners = new Map void>() export type EngineHttpResponse = { status: number @@ -12,7 +12,7 @@ export type EngineHttpResponse = { headers: Record } -type EngineResponseWithId = { +export type EngineResponseWithId = { requestId: string httpResponse: EngineHttpResponse } @@ -24,6 +24,9 @@ export const engineResponseWatcher = { getHandlerId(): string { return HANDLER_ID }, + removeListener(requestId: string): void { + listeners.delete(requestId) + }, async init(): Promise { logger.info('[engineWatcher#init] Initializing engine run watcher') @@ -33,8 +36,7 @@ export const engineResponseWatcher = { const parsedMessasge: EngineResponseWithId = JSON.parse(message) const listener = listeners.get(parsedMessasge.requestId) if (listener) { - listener(parsedMessasge.httpResponse) - listeners.delete(parsedMessasge.requestId) + listener(parsedMessasge) } logger.info( `[engineWatcher#init] message=${parsedMessasge.requestId}`, @@ -42,28 +44,37 @@ export const engineResponseWatcher = { }, ) }, - async listen(requestId: string, timeoutRequest: boolean): Promise { + listen(requestId: string): TypedEventEmitter { + const eventEmitter = new TypedEventEmitter() + listeners.set(requestId, (data) => { + eventEmitter.emit(data) + }) + return eventEmitter + }, + async oneTimeListener(requestId: string, timeoutRequest: boolean): Promise { logger.info(`[engineWatcher#listen] requestId=${requestId}`) return new Promise((resolve) => { - const defaultResponse: EngineHttpResponse = { - status: StatusCodes.NO_CONTENT, - body: {}, - headers: {}, - } - const responseHandler = (flowResponse: EngineHttpResponse) => { - clearTimeout(timeout) - resolve(flowResponse) - } let timeout: NodeJS.Timeout - if (!timeoutRequest) { - listeners.set(requestId, resolve) - } - else { + if (timeoutRequest) { + const defaultResponse: EngineHttpResponse = { + status: StatusCodes.NO_CONTENT, + body: {}, + headers: {}, + } timeout = setTimeout(() => { + this.removeListener(requestId) resolve(defaultResponse) }, WEBHOOK_TIMEOUT_MS) - listeners.set(requestId, responseHandler) + + } + const responseHandler = (flowResponse: EngineResponseWithId) => { + if (timeout) { + clearTimeout(timeout) + } + this.removeListener(requestId) + resolve(flowResponse.httpResponse) } + listeners.set(requestId, responseHandler) }) }, async publish( diff --git a/packages/server/shared/package.json b/packages/server/shared/package.json index ef59338eff..8dbc4602ec 100644 --- a/packages/server/shared/package.json +++ b/packages/server/shared/package.json @@ -6,7 +6,8 @@ "@sentry/node": "7.64.0", "pino": "8.18.0", "pino-loki": "2.1.3", - "@activepieces/shared": "*" + "@activepieces/shared": "*", + "events": "3.3.0" }, "type": "commonjs", "main": "./src/index.js", diff --git a/packages/server/shared/src/index.ts b/packages/server/shared/src/index.ts index 9b0e318118..2821662697 100644 --- a/packages/server/shared/src/index.ts +++ b/packages/server/shared/src/index.ts @@ -1,3 +1,5 @@ +export * from './lib/exception-handler' +export * from './lib/typed-event-emitter' export * from './lib/semaphore' export * from './lib/file-compressor' export * from './lib/file-system' @@ -5,5 +7,4 @@ export * from './lib/package-manager' export * from './lib/system/system' export * from './lib/system/system-prop' export * from './lib/promise-handler' -export * from './lib/logger' -export * from './lib/exception-handler' \ No newline at end of file +export * from './lib/logger' \ No newline at end of file diff --git a/packages/server/shared/src/lib/typed-event-emitter.ts b/packages/server/shared/src/lib/typed-event-emitter.ts new file mode 100644 index 0000000000..46b6c74a45 --- /dev/null +++ b/packages/server/shared/src/lib/typed-event-emitter.ts @@ -0,0 +1,17 @@ +import EventEmitter from 'events' + +export class TypedEventEmitter { + private emitter = new EventEmitter() + + emit(eventArg: TEventData) { + this.emitter.emit('event', eventArg) + } + + on(handler: (eventArg: TEventData) => void) { + this.emitter.on('event', handler) + } + + off(handler: (eventArg: TEventData) => void) { + this.emitter.off('event', handler) + } +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 93a9663be2..879918c2ba 100755 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -60,6 +60,7 @@ export { DelayPauseMetadata, PauseMetadata, WebhookPauseMetadata } from './lib/f export * from './lib/federated-authn' export { STORE_KEY_MAX_LENGTH } from './lib/store-entry/store-entry' export { RetryFlowRequestBody } from './lib/flow-run/test-flow-run-request' +export * from './lib/flow-run/flow-status' export * from './lib/flows/dto/flow-template-request' // Look at https://github.com/sinclairzx81/typebox/issues/350 TypeSystem.ExactOptionalPropertyTypes = false diff --git a/packages/shared/src/lib/flow-run/execution/flow-execution.ts b/packages/shared/src/lib/flow-run/execution/flow-execution.ts index a1adc6e3a5..cbca52894e 100644 --- a/packages/shared/src/lib/flow-run/execution/flow-execution.ts +++ b/packages/shared/src/lib/flow-run/execution/flow-execution.ts @@ -29,6 +29,7 @@ export const WebhookPauseMetadata = Type.Object({ type: Type.Literal(PauseType.WEBHOOK), requestId: Type.String(), response: Type.Unknown(), + handlerId: Type.Optional(Type.String({})), }) export type WebhookPauseMetadata = Static diff --git a/packages/shared/src/lib/flow-run/flow-status.ts b/packages/shared/src/lib/flow-run/flow-status.ts new file mode 100644 index 0000000000..24411d3909 --- /dev/null +++ b/packages/shared/src/lib/flow-run/flow-status.ts @@ -0,0 +1,5 @@ +import { FlowRunStatus } from './execution/flow-execution' + +export const isFlowStateTerminal = (status: FlowRunStatus): boolean => { + return status === FlowRunStatus.SUCCEEDED || status === FlowRunStatus.FAILED || status === FlowRunStatus.INTERNAL_ERROR || status === FlowRunStatus.QUOTA_EXCEEDED +} \ No newline at end of file diff --git a/packages/shared/src/lib/websocket/index.ts b/packages/shared/src/lib/websocket/index.ts index cc85d14a77..34156832c2 100644 --- a/packages/shared/src/lib/websocket/index.ts +++ b/packages/shared/src/lib/websocket/index.ts @@ -2,7 +2,7 @@ export enum WebsocketClientEvent { TEST_FLOW_RUN_STARTED = 'TEST_FLOW_RUN_STARTED', - TEST_FLOW_RUN_FINISHED = 'TEST_FLOW_RUN_FINISHED', + TEST_FLOW_RUN_PROGRESS = 'TEST_FLOW_RUN_PROGRESS', GENERATE_CODE_FINISHED = 'GENERATE_CODE_FINIISHED', TEST_STEP_FINISHED = 'TEST_STEP_FINISHED', } diff --git a/packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts b/packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts index 8d161b2119..56d34955ba 100644 --- a/packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts +++ b/packages/ui/feature-builder-canvas/src/lib/components/widgets/test-flow-widget/test-flow-widget.component.ts @@ -112,7 +112,7 @@ export class TestFlowWidgetComponent implements OnInit { ); this.testResult$ = this.websockService.socket - .fromEvent(WebsocketClientEvent.TEST_FLOW_RUN_FINISHED) + .fromEvent(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS) .pipe( switchMap((flowRun) => { return this.instanceRunService.get(flowRun.id); diff --git a/packages/ui/feature-builder-form-controls/src/lib/components/new-piece-properties-form/piece-properties-form.component.html b/packages/ui/feature-builder-form-controls/src/lib/components/new-piece-properties-form/piece-properties-form.component.html index 01b1338bf0..751e174b9d 100644 --- a/packages/ui/feature-builder-form-controls/src/lib/components/new-piece-properties-form/piece-properties-form.component.html +++ b/packages/ui/feature-builder-form-controls/src/lib/components/new-piece-properties-form/piece-properties-form.component.html @@ -1,6 +1,6 @@
@for ( - property of sortedPropertiesByRequired | objectToArray; + property of propertiesMap | objectToArray; track property.key ) { @if ( @@ -179,6 +179,7 @@ @case (PropertyType.MARKDOWN) { @if (property.value.description) { Duration: - {{ (selectedStepResult.duration || 1) | number: '1.0-1' }} ms + {{ ((selectedStepResult.duration || 1) / 1000) | number: '1.1-3' }} s
[] = steps.map((step) => this.pieceService.getStepDetails(step)); this.urlsToLoad$ = forkJoin(stepMetadata$).pipe( map((items) => items.map((item) => item.logoUrl!).slice(0, this.maxNumberOfIconsToLoad))); @@ -46,11 +50,29 @@ export class PiecesIconsFromFlowComponent implements OnInit { const stepsAppsNames = items.map((item) => item.name); if (stepsAppsNames.length === 1) { return stepsAppsNames[0]!; - } else if (stepsAppsNames.length < 7) { + } else if (stepsAppsNames.length <= 7) { return stepsAppsNames.slice(0, stepsAppsNames.length - 1).join(', ') + ` and ${stepsAppsNames[stepsAppsNames.length - 1]}`; } else { return stepsAppsNames.join(', ') + ' and others'; } } + + private filterOutDuplicates(steps: (Action | Trigger)[]) + { + const seen = new Set(); + return steps.filter((step) => { + let isDuplicate = false; + if(step.type === ActionType.PIECE || step.type === TriggerType.PIECE) + { + isDuplicate = seen.has(step.settings.pieceName); + seen.add(step.settings.pieceName); + } + else{ + isDuplicate = seen.has(step.type); + seen.add(step.type); + } + return !isDuplicate; + }); + } } diff --git a/tsconfig.base.json b/tsconfig.base.json index 9f816691fc..fa047d7160 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -508,6 +508,9 @@ "@activepieces/piece-tally": [ "packages/pieces/community/tally/src/index.ts" ], + "@activepieces/piece-taskade": [ + "packages/pieces/community/taskade/src/index.ts" + ], "@activepieces/piece-telegram-bot": [ "packages/pieces/community/telegram-bot/src/index.ts" ], @@ -556,6 +559,9 @@ "@activepieces/piece-whatsable": [ "packages/pieces/community/whatsable/src/index.ts" ], + "@activepieces/piece-whatsapp": [ + "packages/pieces/community/whatsapp/src/index.ts" + ], "@activepieces/piece-woocommerce": [ "packages/pieces/community/woocommerce/src/index.ts" ],