From 5430082a62b026dfde0ace8c9e07170b976be836 Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Sun, 5 May 2024 11:40:40 +0000 Subject: [PATCH 01/50] feat: test webhook url --- .../webhook/src/lib/triggers/catch-hook.ts | 14 +++++++++----- .../src/lib/pipes/replace-markdown-consts.pipe.ts | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) 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..42e0b26f5a 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 +\`\`\` +***This URL can be used to test the webhook without triggering 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/ui/feature-builder-form-controls/src/lib/pipes/replace-markdown-consts.pipe.ts b/packages/ui/feature-builder-form-controls/src/lib/pipes/replace-markdown-consts.pipe.ts index a6d8911d1c..020a8de557 100644 --- a/packages/ui/feature-builder-form-controls/src/lib/pipes/replace-markdown-consts.pipe.ts +++ b/packages/ui/feature-builder-form-controls/src/lib/pipes/replace-markdown-consts.pipe.ts @@ -13,7 +13,7 @@ export class ReplaceMarkdownConstsPipe implements PipeTransform { formPieceTriggerPrefix: string ): string { return value - .replace('{{webhookUrl}}', `${webhookPrefix}/${flowId}`) - .replace('{{formUrl}}', `${formPieceTriggerPrefix}/${flowId}`); + .replaceAll('{{webhookUrl}}', `${webhookPrefix}/${flowId}`) + .replaceAll('{{formUrl}}', `${formPieceTriggerPrefix}/${flowId}`); } } From a6e88ca2289064513667cef29b3f7f7981d0e2d4 Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Sun, 5 May 2024 18:46:41 +0000 Subject: [PATCH 02/50] feat: use test URL webhook and run webhook when flow is back to draft --- .../security/authz/project-authz-handler.ts | 2 +- .../src/app/webhooks/webhook-controller.ts | 2 +- .../api/src/app/webhooks/webhook-service.ts | 67 ++++++------------- .../webhook-simulation-service.ts | 2 +- 4 files changed, 24 insertions(+), 49 deletions(-) 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/webhooks/webhook-controller.ts b/packages/server/api/src/app/webhooks/webhook-controller.ts index 228d377fa0..c5f117765a 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, diff --git a/packages/server/api/src/app/webhooks/webhook-service.ts b/packages/server/api/src/app/webhooks/webhook-service.ts index 8c28f79133..c1adac7d47 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' @@ -70,22 +69,13 @@ export const webhookService = { logger.info( `[WebhookService#callback] flowInstance not found, 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( @@ -104,20 +94,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 +170,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 +201,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 }, }) From ec1d3c8db8b6ffdd793ee46f3fce62f7bd0fae66 Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Sun, 5 May 2024 20:19:17 +0000 Subject: [PATCH 03/50] feat: if flow not enabled, return 404 --- .../server/api/src/app/webhooks/webhook-service.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/server/api/src/app/webhooks/webhook-service.ts b/packages/server/api/src/app/webhooks/webhook-service.ts index c1adac7d47..0c4b89cf3d 100644 --- a/packages/server/api/src/app/webhooks/webhook-service.ts +++ b/packages/server/api/src/app/webhooks/webhook-service.ts @@ -65,9 +65,9 @@ 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}`, ) throw new ActivepiecesError({ @@ -77,12 +77,6 @@ export const webhookService = { }, }) } - 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( flow.publishedVersionId, From 48ec0cf2d146512be0db451217ddd1add479a9cb Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Sun, 5 May 2024 20:19:41 +0000 Subject: [PATCH 04/50] chore: update webhook piece --- packages/pieces/community/webhook/package.json | 2 +- packages/pieces/community/webhook/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pieces/community/webhook/package.json b/packages/pieces/community/webhook/package.json index 2cb8dfae35..675fda0e6c 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" + "version": "0.2.2" } \ No newline at end of file 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: [], From acdd39eb7fc9b3d2b7bcd5724acc808b4a5485fd Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Mon, 6 May 2024 12:44:09 +0530 Subject: [PATCH 05/50] feat(openai): extract data from text action --- packages/pieces/community/openai/package.json | 2 +- packages/pieces/community/openai/src/index.ts | 127 ++++++++------- .../actions/extract-structure-data.action.ts | 154 ++++++++++++++++++ 3 files changed, 223 insertions(+), 60 deletions(-) create mode 100644 packages/pieces/community/openai/src/lib/actions/extract-structure-data.action.ts diff --git a/packages/pieces/community/openai/package.json b/packages/pieces/community/openai/package.json index 4f1c94d95a..b956d5a1f9 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.22" + "version": "0.3.23" } \ No newline at end of file diff --git a/packages/pieces/community/openai/src/index.ts b/packages/pieces/community/openai/src/index.ts index 3dc5c44794..e511fb6c16 100644 --- a/packages/pieces/community/openai/src/index.ts +++ b/packages/pieces/community/openai/src/index.ts @@ -1,13 +1,10 @@ import { - AuthenticationType, - HttpMethod, - createCustomApiCallAction, - httpClient, + AuthenticationType, + HttpMethod, + createCustomApiCallAction, + httpClient, } from '@activepieces/pieces-common'; -import { - PieceAuth, - createPiece, -} from '@activepieces/pieces-framework'; +import { PieceAuth, createPiece } from '@activepieces/pieces-framework'; import { PieceCategory } from '@activepieces/shared'; import { askAssistant } from './lib/actions/ask-assistant'; import { generateImage } from './lib/actions/generate-image'; @@ -17,6 +14,7 @@ import { transcribeAction } from './lib/actions/transcriptions'; import { translateAction } from './lib/actions/translation'; import { visionPrompt } from './lib/actions/vision-prompt'; import { baseUrl } from './lib/common/common'; +import { extractStructureDataAction } from './lib/actions/extract-structure-data.action'; const markdownDescription = ` Follow these instructions to get your OpenAI API Key: @@ -28,58 +26,69 @@ It is strongly recommended that you add your credit card information to your Ope `; export const openaiAuth = PieceAuth.SecretText({ - description: markdownDescription, - displayName: 'API Key', - required: true, - validate: async (auth) => { - try { - await httpClient.sendRequest<{ - data: { id: string }[]; - }>({ - url: `${baseUrl}/models`, - method: HttpMethod.GET, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: auth.auth as string, - }, - }); - return { - valid: true, - }; - } catch (e) { - return { - valid: false, - error: 'Invalid API key', - }; - } - }, + description: markdownDescription, + displayName: 'API Key', + required: true, + validate: async (auth) => { + try { + await httpClient.sendRequest<{ + data: { id: string }[]; + }>({ + url: `${baseUrl}/models`, + method: HttpMethod.GET, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: auth.auth as string, + }, + }); + return { + valid: true, + }; + } catch (e) { + return { + valid: false, + error: 'Invalid API key', + }; + } + }, }); export const openai = createPiece({ - displayName: 'OpenAI', - description: 'Use the many tools ChatGPT has to offer.', - minimumSupportedRelease: '0.5.0', - logoUrl: 'https://cdn.activepieces.com/pieces/openai.png', - categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE], - auth: openaiAuth, - actions: [ - askOpenAI, - askAssistant, - generateImage, - visionPrompt, - textToSpeech, - transcribeAction, - translateAction, - createCustomApiCallAction({ - auth: openaiAuth, - baseUrl: () => baseUrl, - authMapping: (auth) => { - return { - Authorization: `Bearer ${auth}`, - }; - }, - }), - ], - authors: ["aboudzein","astorozhevsky","Willianwg","Nilesh","Salem-Alaa","kishanprmr","MoShizzle","khaledmashaly","abuaboud"], - triggers: [], + displayName: 'OpenAI', + description: 'Use the many tools ChatGPT has to offer.', + minimumSupportedRelease: '0.5.0', + logoUrl: 'https://cdn.activepieces.com/pieces/openai.png', + categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE], + auth: openaiAuth, + actions: [ + askOpenAI, + askAssistant, + generateImage, + visionPrompt, + textToSpeech, + transcribeAction, + translateAction, + extractStructureDataAction, + createCustomApiCallAction({ + auth: openaiAuth, + baseUrl: () => baseUrl, + authMapping: (auth) => { + return { + Authorization: `Bearer ${auth}`, + }; + }, + }), + ], + authors: [ + 'aboudzein', + 'astorozhevsky', + 'Willianwg', + 'Nilesh', + 'Salem-Alaa', + 'kishanprmr', + 'MoShizzle', + 'khaledmashaly', + 'abuaboud', + ], + triggers: [], }); 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 new file mode 100644 index 0000000000..05575055f8 --- /dev/null +++ b/packages/pieces/community/openai/src/lib/actions/extract-structure-data.action.ts @@ -0,0 +1,154 @@ +import { openaiAuth } from '../../'; +import { createAction, Property } from '@activepieces/pieces-framework'; +import OpenAI from 'openai'; +import { notLLMs } from '../common/common'; + +export const extractStructureDataAction = createAction({ + auth: openaiAuth, + name: 'openai-extract-structure-data', + displayName: 'Transform Text to Structure Data', + description: 'Returns structured data from provided unstructured text.', + props: { + model: Property.Dropdown({ + displayName: 'Model', + required: true, + refreshers: [], + defaultValue: 'gpt-3.5-turbo', + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + placeholder: 'Enter your API key first', + options: [], + }; + } + try { + const openai = new OpenAI({ + apiKey: auth as string, + }); + const response = await openai.models.list(); + // We need to get only LLM models + const models = response.data.filter((model) => !notLLMs.includes(model.id)); + return { + disabled: false, + options: models.map((model) => { + return { + label: model.id, + value: model.id, + }; + }), + }; + } catch (error) { + return { + disabled: true, + options: [], + placeholder: "Couldn't load models, API key is invalid", + }; + } + }, + }), + text: Property.LongText({ + 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', + 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. ', + required: true, + }), + propDescription: Property.LongText({ + displayName: 'Description', + description: + 'Brief description of the parameter, defining what data will be extracted to this parameter.', + required: true, + }), + propDataType: Property.StaticDropdown({ + displayName: 'Data Type', + description: 'Type of parameter.', + required: true, + defaultValue: 'string', + options: { + disabled: false, + options: [ + { label: 'string', value: 'string' }, + { label: 'number', value: 'number' }, + { label: 'boolean', value: 'boolean' }, + ], + }, + }), + propIsRequired: Property.Checkbox({ + displayName: 'Is Parameter Required?', + required: true, + defaultValue: true, + }), + }, + }), + }, + async run(context) { + try { + const { model, text, prompt } = context.propsValue; + + const paramInputArray = context.propsValue.params as ParamInput[]; + + const functionParams: Record = {}; + const requiredFunctionParams = []; + for (const param of paramInputArray) { + functionParams[param.propName] = { + type: param.propDataType, + description: param.propDescription, + }; + if (param.propIsRequired) requiredFunctionParams.push(param.propName); + } + + const openai = new OpenAI({ + apiKey: context.auth, + }); + + const response = await openai.chat.completions.create({ + model: model, + messages: [{ role: 'user', content: text }], + tools: [ + { + type: 'function', + function: { + name: 'extract_structured_data', + description: prompt, + parameters: { + type: 'object', + properties: functionParams, + required: requiredFunctionParams, + }, + }, + }, + ], + }); + + const toolCallsResponse = response.choices[0].message.tool_calls; + if (toolCallsResponse) { + return JSON.parse(toolCallsResponse[0].function.arguments); + } else { + return response.choices[0].message; + } + } catch (error) { + throw Error(JSON.stringify(error, null, 2)); + } + }, +}); + +interface ParamInput { + propName: string; + propDescription: string; + propDataType: string; + propIsRequired: boolean; +} From 8948c21a00684defe056552e2f3eb8829dffeeea Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Mon, 6 May 2024 13:31:03 +0530 Subject: [PATCH 06/50] fix(openai): fix typos --- packages/pieces/community/openai/src/index.ts | 4 +- .../actions/extract-structure-data.action.ts | 80 +++++++++---------- 2 files changed, 40 insertions(+), 44 deletions(-) diff --git a/packages/pieces/community/openai/src/index.ts b/packages/pieces/community/openai/src/index.ts index e511fb6c16..2359fc6152 100644 --- a/packages/pieces/community/openai/src/index.ts +++ b/packages/pieces/community/openai/src/index.ts @@ -14,7 +14,7 @@ import { transcribeAction } from './lib/actions/transcriptions'; import { translateAction } from './lib/actions/translation'; import { visionPrompt } from './lib/actions/vision-prompt'; import { baseUrl } from './lib/common/common'; -import { extractStructureDataAction } from './lib/actions/extract-structure-data.action'; +import { extractStructuredDataAction } from './lib/actions/extract-structure-data.action'; const markdownDescription = ` Follow these instructions to get your OpenAI API Key: @@ -68,7 +68,7 @@ export const openai = createPiece({ textToSpeech, transcribeAction, translateAction, - extractStructureDataAction, + extractStructuredDataAction, createCustomApiCallAction({ auth: openaiAuth, baseUrl: () => baseUrl, 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 05575055f8..7abf87983f 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 @@ -3,10 +3,10 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import OpenAI from 'openai'; import { notLLMs } from '../common/common'; -export const extractStructureDataAction = createAction({ +export const extractStructuredDataAction = createAction({ auth: openaiAuth, - name: 'openai-extract-structure-data', - displayName: 'Transform Text to Structure Data', + name: 'openai-extract-structured-data', + displayName: 'Transform Text to Structured Data', description: 'Returns structured data from provided unstructured text.', props: { model: Property.Dropdown({ @@ -96,52 +96,48 @@ export const extractStructureDataAction = createAction({ }), }, async run(context) { - try { - const { model, text, prompt } = context.propsValue; + const { model, text, prompt } = context.propsValue; - const paramInputArray = context.propsValue.params as ParamInput[]; + const paramInputArray = context.propsValue.params as ParamInput[]; - const functionParams: Record = {}; - const requiredFunctionParams = []; - for (const param of paramInputArray) { - functionParams[param.propName] = { - type: param.propDataType, - description: param.propDescription, - }; - if (param.propIsRequired) requiredFunctionParams.push(param.propName); - } + const functionParams: Record = {}; + const requiredFunctionParams = []; + for (const param of paramInputArray) { + functionParams[param.propName] = { + type: param.propDataType, + description: param.propDescription, + }; + if (param.propIsRequired) requiredFunctionParams.push(param.propName); + } - const openai = new OpenAI({ - apiKey: context.auth, - }); + const openai = new OpenAI({ + apiKey: context.auth, + }); - const response = await openai.chat.completions.create({ - model: model, - messages: [{ role: 'user', content: text }], - tools: [ - { - type: 'function', - function: { - name: 'extract_structured_data', - description: prompt, - parameters: { - type: 'object', - properties: functionParams, - required: requiredFunctionParams, - }, + const response = await openai.chat.completions.create({ + model: model, + messages: [{ role: 'user', content: text }], + tools: [ + { + type: 'function', + function: { + name: 'extract_structured_data', + description: prompt, + parameters: { + type: 'object', + properties: functionParams, + required: requiredFunctionParams, }, }, - ], - }); + }, + ], + }); - const toolCallsResponse = response.choices[0].message.tool_calls; - if (toolCallsResponse) { - return JSON.parse(toolCallsResponse[0].function.arguments); - } else { - return response.choices[0].message; - } - } catch (error) { - throw Error(JSON.stringify(error, null, 2)); + const toolCallsResponse = response.choices[0].message.tool_calls; + if (toolCallsResponse) { + return JSON.parse(toolCallsResponse[0].function.arguments); + } else { + throw Error('Unable to extract data. Please provide valid params and text.'); } }, }); From cc0c96656edae51f4f6612afd538a234ac4884f2 Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Mon, 6 May 2024 14:19:50 +0530 Subject: [PATCH 07/50] feat(wordpress): add ACF field object --- packages/pieces/community/wordpress/package.json | 2 +- .../src/lib/actions/create-post.action.ts | 14 ++++++++++++++ .../src/lib/actions/update-post.action.ts | 13 +++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/pieces/community/wordpress/package.json b/packages/pieces/community/wordpress/package.json index 340d897c2f..d219e9f0e3 100644 --- a/packages/pieces/community/wordpress/package.json +++ b/packages/pieces/community/wordpress/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-wordpress", - "version": "0.3.13" + "version": "0.3.14" } \ No newline at end of file diff --git a/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts b/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts index 6fd5849c1f..bb620f1927 100644 --- a/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts +++ b/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts @@ -39,6 +39,12 @@ export const createWordPressPost = createAction({ }), featured_media_file: wordpressCommon.featured_media_file, tags: wordpressCommon.tags, + acfFields: Property.Object({ + displayName: 'Custom ACF fields', + description: + 'Provide field name with value.You can find out field name from ACF plugin menu.', + required: false, + }), categories: wordpressCommon.categories, featured_media: wordpressCommon.featured_media, status: wordpressCommon.status, @@ -57,6 +63,7 @@ export const createWordPressPost = createAction({ }), }, async run(context) { + console.log(JSON.stringify(context.propsValue)); if (!(await wordpressCommon.urlExists(context.auth.website_url.trim()))) { throw new Error('Website url is invalid: ' + context.auth.website_url); } @@ -93,6 +100,13 @@ export const createWordPressPost = createAction({ requestBody['featured_media'] = context.propsValue.featured_media; } + if ( + context.propsValue.acfFields && + Object.keys(context.propsValue.acfFields).length > 0 + ) { + requestBody['acf'] = context.propsValue.acfFields; + } + if (context.propsValue.featured_media_file) { const formData = new FormData(); const { filename, base64 } = context.propsValue.featured_media_file; diff --git a/packages/pieces/community/wordpress/src/lib/actions/update-post.action.ts b/packages/pieces/community/wordpress/src/lib/actions/update-post.action.ts index 2cb42a7832..eeb89a55a0 100644 --- a/packages/pieces/community/wordpress/src/lib/actions/update-post.action.ts +++ b/packages/pieces/community/wordpress/src/lib/actions/update-post.action.ts @@ -36,6 +36,12 @@ export const updateWordPressPost = createAction({ }), featured_media_file: wordpressCommon.featured_media_file, tags: wordpressCommon.tags, + acfFields: Property.Object({ + displayName: 'Custom ACF fields', + description: + 'Provide field name with value.You can find out field name from ACF plugin menu.', + required: false, + }), categories: wordpressCommon.categories, featured_media: wordpressCommon.featured_media, status: wordpressCommon.status, @@ -99,6 +105,13 @@ export const updateWordPressPost = createAction({ requestBody['featured_media'] = context.propsValue.featured_media; } + if ( + context.propsValue.acfFields && + Object.keys(context.propsValue.acfFields).length > 0 + ) { + requestBody['acf'] = context.propsValue.acfFields; + } + if (context.propsValue.featured_media_file) { const formData = new FormData(); const { filename, base64 } = context.propsValue.featured_media_file; From 14d8acb321164c215288f69cd2bd3c0387e777e2 Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Mon, 6 May 2024 14:37:31 +0530 Subject: [PATCH 08/50] chore: remove console message --- .../community/wordpress/src/lib/actions/create-post.action.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts b/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts index bb620f1927..d14ca4f8c9 100644 --- a/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts +++ b/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts @@ -63,7 +63,6 @@ export const createWordPressPost = createAction({ }), }, async run(context) { - console.log(JSON.stringify(context.propsValue)); if (!(await wordpressCommon.urlExists(context.auth.website_url.trim()))) { throw new Error('Website url is invalid: ' + context.auth.website_url); } From e71d3463da1b82315091056e85effc1b9d75cffb Mon Sep 17 00:00:00 2001 From: Abdelrahman Mostafa Date: Mon, 6 May 2024 14:46:08 +0000 Subject: [PATCH 09/50] feat(whatsapp): add sendMessage, sendMedia actions --- .../pieces/community/whatsapp/.eslintrc.json | 33 +++++++++ packages/pieces/community/whatsapp/README.md | 7 ++ .../pieces/community/whatsapp/package.json | 4 ++ .../pieces/community/whatsapp/project.json | 38 ++++++++++ .../pieces/community/whatsapp/src/index.ts | 48 +++++++++++++ .../whatsapp/src/lib/actions/send-media.ts | 70 +++++++++++++++++++ .../whatsapp/src/lib/actions/send-message.ts | 43 ++++++++++++ .../whatsapp/src/lib/common/utils.ts | 3 + .../pieces/community/whatsapp/tsconfig.json | 19 +++++ .../community/whatsapp/tsconfig.lib.json | 11 +++ tsconfig.base.json | 3 + 11 files changed, 279 insertions(+) create mode 100644 packages/pieces/community/whatsapp/.eslintrc.json create mode 100644 packages/pieces/community/whatsapp/README.md create mode 100644 packages/pieces/community/whatsapp/package.json create mode 100644 packages/pieces/community/whatsapp/project.json create mode 100644 packages/pieces/community/whatsapp/src/index.ts create mode 100644 packages/pieces/community/whatsapp/src/lib/actions/send-media.ts create mode 100644 packages/pieces/community/whatsapp/src/lib/actions/send-message.ts create mode 100644 packages/pieces/community/whatsapp/src/lib/common/utils.ts create mode 100644 packages/pieces/community/whatsapp/tsconfig.json create mode 100644 packages/pieces/community/whatsapp/tsconfig.lib.json 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..095098bacc --- /dev/null +++ b/packages/pieces/community/whatsapp/src/index.ts @@ -0,0 +1,48 @@ + +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 Client ID and Client Secret: + +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. Fill the App Domain with Domain in Redirect URL. +5. Add new Product -> WhatsApp. +6. Navigate to WhatsApp Settings > API Setup. +7. Select a phone number and copy its Phone number ID. +8. Login to your (Meta Business Manager)[https://business.facebook.com/]. +9. Click on Settings. +10. 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", + 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/tsconfig.base.json b/tsconfig.base.json index acf980862d..5b770d36de 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -553,6 +553,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" ], From 91988e420988d77e22de6a05398492cb192a32bc Mon Sep 17 00:00:00 2001 From: Abdelrahman Mostafa Date: Mon, 6 May 2024 14:57:22 +0000 Subject: [PATCH 10/50] fix(whatsapp): description markdown --- packages/pieces/community/whatsapp/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pieces/community/whatsapp/src/index.ts b/packages/pieces/community/whatsapp/src/index.ts index 095098bacc..0ac4239de9 100644 --- a/packages/pieces/community/whatsapp/src/index.ts +++ b/packages/pieces/community/whatsapp/src/index.ts @@ -13,7 +13,7 @@ To Obtain a Client ID and Client Secret: 5. Add new Product -> WhatsApp. 6. Navigate to WhatsApp Settings > API Setup. 7. Select a phone number and copy its Phone number ID. -8. Login to your (Meta Business Manager)[https://business.facebook.com/]. +8. Login to your [Meta Business Manager](https://business.facebook.com/). 9. Click on Settings. 10. Create a new System User with access over the app and copy the access token. `; From 080d2b51f19b2a347458e14ec6cafac90da55bee Mon Sep 17 00:00:00 2001 From: Abdelrahman Mostafa Date: Mon, 6 May 2024 18:47:28 +0000 Subject: [PATCH 11/50] fix(whatsapp): description markdown --- packages/pieces/community/whatsapp/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pieces/community/whatsapp/src/index.ts b/packages/pieces/community/whatsapp/src/index.ts index 0ac4239de9..900909aab9 100644 --- a/packages/pieces/community/whatsapp/src/index.ts +++ b/packages/pieces/community/whatsapp/src/index.ts @@ -4,7 +4,7 @@ import { sendMessage } from "./lib/actions/send-message"; import { sendMedia } from "./lib/actions/send-media"; const markdown = ` -To Obtain a Client ID and Client Secret: +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. From 4cf9712f3177f8d8d1d20fb5157f8b6382ac666f Mon Sep 17 00:00:00 2001 From: Abdelrahman Mostafa Date: Mon, 6 May 2024 18:48:59 +0000 Subject: [PATCH 12/50] fix(whatsapp): description markdown --- packages/pieces/community/whatsapp/src/index.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/pieces/community/whatsapp/src/index.ts b/packages/pieces/community/whatsapp/src/index.ts index 900909aab9..e9a7a14a1b 100644 --- a/packages/pieces/community/whatsapp/src/index.ts +++ b/packages/pieces/community/whatsapp/src/index.ts @@ -9,13 +9,12 @@ To Obtain a Phone Number ID and a Permanent System User Access Token, follow the 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. Fill the App Domain with Domain in Redirect URL. -5. Add new Product -> WhatsApp. -6. Navigate to WhatsApp Settings > API Setup. -7. Select a phone number and copy its Phone number ID. -8. Login to your [Meta Business Manager](https://business.facebook.com/). -9. Click on Settings. -10. Create a new System User with access over the app and copy the access token. +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({ From 7a796363aac1088b3d255180d2a80944b61506ea Mon Sep 17 00:00:00 2001 From: Abdul-rahman Yasir Khalil Date: Tue, 7 May 2024 12:05:42 +0300 Subject: [PATCH 13/50] fix: activate request trial for git sync, project members and audit logs --- .../audit-event-table/audit-event-table.component.html | 3 ++- .../project-members-table.component.html | 1 + .../lib/components/upgrade-note/upgrade-note.component.ts | 7 +------ .../components/sync-project/sync-project.component.html | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/ee/ui/platform/src/lib/components/audit-event-table/audit-event-table.component.html b/packages/ee/ui/platform/src/lib/components/audit-event-table/audit-event-table.component.html index 8c8a0790ec..a1a145189a 100644 --- a/packages/ee/ui/platform/src/lib/components/audit-event-table/audit-event-table.component.html +++ b/packages/ee/ui/platform/src/lib/components/audit-event-table/audit-event-table.component.html @@ -1,7 +1,8 @@
@if(featureLocked) { + featureKey="AUDIT_LOGS" + > } @else { diff --git a/packages/ee/ui/project-members/src/lib/project-members-table/project-members-table.component.html b/packages/ee/ui/project-members/src/lib/project-members-table/project-members-table.component.html index c249c22480..f5f6580a97 100644 --- a/packages/ee/ui/project-members/src/lib/project-members-table/project-members-table.component.html +++ b/packages/ee/ui/project-members/src/lib/project-members-table/project-members-table.component.html @@ -2,6 +2,7 @@ @if(isFeatureLocked) {
diff --git a/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.ts b/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.ts index 33e2e50193..9e1b7fa8a4 100644 --- a/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.ts +++ b/packages/ui/common/src/lib/components/upgrade-note/upgrade-note.component.ts @@ -12,20 +12,15 @@ import { fadeIn400ms } from '../../animation/fade-in.animations'; animations: [fadeIn400ms], }) export class UpgradeNoteComponent { - @Input() docsLink = ''; @Input({ required: true }) featureNoteTitle = ''; @Input({ required: true }) featureNote = ''; @Input() videoUrl = ''; - @Input() featureKey: FeatureKey; + @Input({ required: true }) featureKey: FeatureKey; constructor(private contactSalesService: ContactSalesService) {} @Input() insideTab = false; - openDocs() { - window.open(this.docsLink, '_blank', 'noopener noreferrer'); - } - openContactSales(): void { this.contactSalesService.open([this.featureKey]); } diff --git a/packages/ui/feature-git-sync/src/lib/components/sync-project/sync-project.component.html b/packages/ui/feature-git-sync/src/lib/components/sync-project/sync-project.component.html index 68cf6538c7..38445d837c 100644 --- a/packages/ui/feature-git-sync/src/lib/components/sync-project/sync-project.component.html +++ b/packages/ui/feature-git-sync/src/lib/components/sync-project/sync-project.component.html @@ -2,7 +2,7 @@ @if(showUpgrade) { + featureKey="GIT_SYNC"> } @else {
Date: Tue, 7 May 2024 14:52:33 +0530 Subject: [PATCH 14/50] feat(todoist): update task,find task and mark task complete action --- .../pieces/community/todoist/package.json | 2 +- .../pieces/community/todoist/src/index.ts | 50 ++-- .../src/lib/actions/create-task-action.ts | 115 +++++---- .../src/lib/actions/find-task.action.ts | 36 +++ .../lib/actions/mark-task-completed.action.ts | 25 ++ .../src/lib/actions/update-task.action.ts | 61 +++++ .../src/lib/common/client/rest-client.ts | 232 +++++++++++------- .../todoist/src/lib/common/models.ts | 96 +++++--- .../community/todoist/src/lib/common/props.ts | 106 +++++--- .../lib/triggers/task-completed-trigger.ts | 96 ++++---- 10 files changed, 528 insertions(+), 291 deletions(-) create mode 100644 packages/pieces/community/todoist/src/lib/actions/find-task.action.ts create mode 100644 packages/pieces/community/todoist/src/lib/actions/mark-task-completed.action.ts create mode 100644 packages/pieces/community/todoist/src/lib/actions/update-task.action.ts diff --git a/packages/pieces/community/todoist/package.json b/packages/pieces/community/todoist/package.json index d3e96128c4..d3dbd499dc 100644 --- a/packages/pieces/community/todoist/package.json +++ b/packages/pieces/community/todoist/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-todoist", - "version": "0.3.7" + "version": "0.3.8" } \ No newline at end of file diff --git a/packages/pieces/community/todoist/src/index.ts b/packages/pieces/community/todoist/src/index.ts index 566ee16d21..51d10edd8a 100644 --- a/packages/pieces/community/todoist/src/index.ts +++ b/packages/pieces/community/todoist/src/index.ts @@ -3,31 +3,37 @@ import { OAuth2PropertyValue, PieceAuth, createPiece } from '@activepieces/piece import { PieceCategory } from '@activepieces/shared'; import { todoistCreateTaskAction } from './lib/actions/create-task-action'; import { todoistTaskCompletedTrigger } from './lib/triggers/task-completed-trigger'; +import { todoistUpdateTaskAction } from './lib/actions/update-task.action'; +import { todoistFindTaskAction } from './lib/actions/find-task.action'; +import { todoistMarkTaskCompletedAction } from './lib/actions/mark-task-completed.action'; export const todoistAuth = PieceAuth.OAuth2({ - required: true, - authUrl: 'https://todoist.com/oauth/authorize', - tokenUrl: 'https://todoist.com/oauth/access_token', - scope: ['data:read_write'], + required: true, + authUrl: 'https://todoist.com/oauth/authorize', + tokenUrl: 'https://todoist.com/oauth/access_token', + scope: ['data:read_write'], }); export const todoist = createPiece({ - displayName: 'Todoist', - description: 'To-do list and task manager', - minimumSupportedRelease: '0.5.0', - logoUrl: 'https://cdn.activepieces.com/pieces/todoist.png', - authors: ["MyWay","kishanprmr","MoShizzle","khaledmashaly","abuaboud"], - categories: [PieceCategory.PRODUCTIVITY], - auth: todoistAuth, - actions: [ - todoistCreateTaskAction, - createCustomApiCallAction({ - baseUrl: () => 'https://api.todoist.com/rest/v2', - auth: todoistAuth, - authMapping: (auth) => ({ - Authorization: `Bearer ${(auth as OAuth2PropertyValue).access_token}`, - }), - }), - ], - triggers: [todoistTaskCompletedTrigger], + displayName: 'Todoist', + description: 'To-do list and task manager', + minimumSupportedRelease: '0.5.0', + logoUrl: 'https://cdn.activepieces.com/pieces/todoist.png', + authors: ['MyWay', 'kishanprmr', 'MoShizzle', 'khaledmashaly', 'abuaboud'], + categories: [PieceCategory.PRODUCTIVITY], + auth: todoistAuth, + actions: [ + todoistCreateTaskAction, + todoistUpdateTaskAction, + todoistFindTaskAction, + todoistMarkTaskCompletedAction, + createCustomApiCallAction({ + baseUrl: () => 'https://api.todoist.com/rest/v2', + auth: todoistAuth, + authMapping: (auth) => ({ + Authorization: `Bearer ${(auth as OAuth2PropertyValue).access_token}`, + }), + }), + ], + triggers: [todoistTaskCompletedTrigger], }); diff --git a/packages/pieces/community/todoist/src/lib/actions/create-task-action.ts b/packages/pieces/community/todoist/src/lib/actions/create-task-action.ts index 566d7a63a8..2f0d7411a9 100644 --- a/packages/pieces/community/todoist/src/lib/actions/create-task-action.ts +++ b/packages/pieces/community/todoist/src/lib/actions/create-task-action.ts @@ -1,70 +1,65 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { assertNotNullOrUndefined } from '@activepieces/shared'; import { todoistRestClient } from '../common/client/rest-client'; -import { todoistProjectIdDropdown } from '../common/props'; +import { todoistProjectIdDropdown, todoistSectionIdDropdown } from '../common/props'; import { TodoistCreateTaskRequest } from '../common/models'; import { todoistAuth } from '../..'; export const todoistCreateTaskAction = createAction({ - auth: todoistAuth, - name: 'create_task', - displayName: 'Create Task', - description: 'Create task', - props: { - project_id: todoistProjectIdDropdown, - content: Property.LongText({ - displayName: 'content', - description: - "The task's content. It may contain some markdown-formatted text and hyperlinks", - required: true, - }), - description: Property.LongText({ - displayName: 'Description', - description: - 'A description for the task. This value may contain some markdown-formatted text and hyperlinks.', - required: false, - }), - labels: Property.Array({ - displayName: 'Labels', - required: false, - description: - "The task's labels (a list of names that may represent either personal or shared labels)", - }), - priority: Property.Number({ - displayName: 'Priority', - description: 'Task priority from 1 (normal) to 4 (urgent)', - required: false, - }), - due_date: Property.ShortText({ - displayName: 'Due date', - description: - "Specific date in YYYY-MM-DD format relative to user's timezone", - required: false, - }), - section_id: Property.ShortText({ - displayName: 'Section', - description: - "A section for the task. It should be a Section ID under the same project", - required: false, - }), - }, + auth: todoistAuth, + name: 'create_task', + displayName: 'Create Task', + description: 'Create task', + props: { + project_id: todoistProjectIdDropdown( + "Task project ID. If not set, task is put to user's Inbox.", + ), + content: Property.LongText({ + displayName: 'content', + description: "The task's content. It may contain some markdown-formatted text and hyperlinks", + required: true, + }), + description: Property.LongText({ + displayName: 'Description', + description: + 'A description for the task. This value may contain some markdown-formatted text and hyperlinks.', + required: false, + }), + labels: Property.Array({ + displayName: 'Labels', + required: false, + description: + "The task's labels (a list of names that may represent either personal or shared labels)", + }), + priority: Property.Number({ + displayName: 'Priority', + description: 'Task priority from 1 (normal) to 4 (urgent)', + required: false, + }), + due_date: Property.ShortText({ + displayName: 'Due date', + description: "Specific date in YYYY-MM-DD format relative to user's timezone", + required: false, + }), + section_id: todoistSectionIdDropdown, + }, - async run({ auth, propsValue }) { - const token = auth.access_token; - const { project_id, content, description, labels, priority, due_date, section_id} = - propsValue as TodoistCreateTaskRequest; + async run({ auth, propsValue }) { + const token = auth.access_token; + const { project_id, content, description, labels, priority, due_date, section_id } = + propsValue as TodoistCreateTaskRequest; - assertNotNullOrUndefined(token, 'token'); - assertNotNullOrUndefined(content, 'content'); - return await todoistRestClient.tasks.create({ - token, - project_id, - content, - description, - labels, - priority, - due_date, - section_id - }); - }, + assertNotNullOrUndefined(token, 'token'); + assertNotNullOrUndefined(content, 'content'); + return await todoistRestClient.tasks.create({ + token, + project_id, + content, + description, + labels, + priority, + due_date, + section_id, + }); + }, }); diff --git a/packages/pieces/community/todoist/src/lib/actions/find-task.action.ts b/packages/pieces/community/todoist/src/lib/actions/find-task.action.ts new file mode 100644 index 0000000000..168efbd65d --- /dev/null +++ b/packages/pieces/community/todoist/src/lib/actions/find-task.action.ts @@ -0,0 +1,36 @@ +import { todoistAuth } from '../..'; +import { createAction, Property } from '@activepieces/pieces-framework'; +import { todoistProjectIdDropdown } from '../common/props'; +import { todoistRestClient } from '../common/client/rest-client'; +import { assertNotNullOrUndefined } from '@activepieces/shared'; + +export const todoistFindTaskAction = createAction({ + auth: todoistAuth, + name: 'find_task', + displayName: 'Find Task', + description: 'Finds a task by name.', + props: { + name: Property.ShortText({ + displayName: 'Name', + description: 'The name of the task to search for.', + required: true, + }), + project_id: todoistProjectIdDropdown( + 'Search for tasks within the selected project. If left blank, then all projects are searched.', + ), + }, + async run(context) { + const token = context.auth.access_token; + const { name, project_id } = context.propsValue; + + assertNotNullOrUndefined(token, 'token'); + const tasks = await todoistRestClient.tasks.list({ token, project_id }); + + let matchedTask = tasks.find((task) => task.content == name); + if (!matchedTask) { + throw new Error('Task not found'); + } else { + return matchedTask; + } + }, +}); diff --git a/packages/pieces/community/todoist/src/lib/actions/mark-task-completed.action.ts b/packages/pieces/community/todoist/src/lib/actions/mark-task-completed.action.ts new file mode 100644 index 0000000000..307ef05fa8 --- /dev/null +++ b/packages/pieces/community/todoist/src/lib/actions/mark-task-completed.action.ts @@ -0,0 +1,25 @@ +import { assertNotNullOrUndefined } from '@activepieces/shared'; +import { todoistAuth } from '../..'; +import { createAction, Property } from '@activepieces/pieces-framework'; +import { todoistRestClient } from '../common/client/rest-client'; + +export const todoistMarkTaskCompletedAction = createAction({ + auth: todoistAuth, + name: 'mark_task_completed', + displayName: 'Mark Task as Completed', + description: 'Marks a task as being completed.', + props: { + task_id: Property.ShortText({ + displayName: 'Task ID', + required: true, + }), + }, + async run(context) { + const token = context.auth.access_token; + const { task_id } = context.propsValue; + + assertNotNullOrUndefined(token, 'token'); + + return await todoistRestClient.tasks.close({ token, task_id }); + }, +}); diff --git a/packages/pieces/community/todoist/src/lib/actions/update-task.action.ts b/packages/pieces/community/todoist/src/lib/actions/update-task.action.ts new file mode 100644 index 0000000000..5dced83b92 --- /dev/null +++ b/packages/pieces/community/todoist/src/lib/actions/update-task.action.ts @@ -0,0 +1,61 @@ +import { createAction, Property } from '@activepieces/pieces-framework'; +import { assertNotNullOrUndefined } from '@activepieces/shared'; +import { todoistRestClient } from '../common/client/rest-client'; +import { todoistAuth } from '../..'; + +export const todoistUpdateTaskAction = createAction({ + auth: todoistAuth, + name: 'update_task', + displayName: 'Update Task', + description: 'Updates an existing task.', + props: { + task_id: Property.ShortText({ + displayName: 'Task ID', + required: true, + }), + content: Property.LongText({ + displayName: 'content', + description: "The task's content. It may contain some markdown-formatted text and hyperlinks", + required: false, + }), + description: Property.LongText({ + displayName: 'Description', + description: + 'A description for the task. This value may contain some markdown-formatted text and hyperlinks.', + required: false, + }), + labels: Property.Array({ + displayName: 'Labels', + required: false, + description: + "The task's labels (a list of names that may represent either personal or shared labels)", + }), + priority: Property.Number({ + displayName: 'Priority', + description: 'Task priority from 1 (normal) to 4 (urgent)', + required: false, + }), + due_date: Property.ShortText({ + displayName: 'Due date', + description: "Specific date in YYYY-MM-DD format relative to user's timezone", + required: false, + }), + }, + + async run({ auth, propsValue }) { + const token = auth.access_token; + const { task_id, content, description, priority, due_date } = propsValue; + const labels = propsValue.labels as string[]; + + assertNotNullOrUndefined(token, 'token'); + return await todoistRestClient.tasks.update({ + token, + task_id, + content, + description, + labels, + priority, + due_date, + }); + }, +}); diff --git a/packages/pieces/community/todoist/src/lib/common/client/rest-client.ts b/packages/pieces/community/todoist/src/lib/common/client/rest-client.ts index 8e54bb1fe0..fdd6d4b411 100644 --- a/packages/pieces/community/todoist/src/lib/common/client/rest-client.ts +++ b/packages/pieces/community/todoist/src/lib/common/client/rest-client.ts @@ -1,104 +1,166 @@ import { - HttpRequest, - HttpMethod, - AuthenticationType, - httpClient, + HttpRequest, + HttpMethod, + AuthenticationType, + httpClient, } from '@activepieces/pieces-common'; import { isNotUndefined, pickBy } from '@activepieces/shared'; import { - TodoistCreateTaskRequest, - TodoistProject, - TodoistTask, + TodoistCreateTaskRequest, + TodoistProject, + TodoistSection, + TodoistTask, + TodoistUpdateTaskRequest, } from '../models'; const API = 'https://api.todoist.com/rest/v2'; export const todoistRestClient = { - projects: { - async list({ token }: ProjectsListParams): Promise { - const request: HttpRequest = { - method: HttpMethod.GET, - url: `${API}/projects`, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token, - }, - }; - - const response = await httpClient.sendRequest(request); - return response.body; - }, - }, - - tasks: { - async create({ - token, - project_id, - content, - description, - labels, - priority, - due_date, - section_id - }: TasksCreateParams): Promise { - const request: HttpRequest = { - method: HttpMethod.POST, - url: `${API}/tasks`, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token, - }, - body: { - content, - project_id, - description, - labels, - priority, - due_date, - section_id - }, - }; - - const response = await httpClient.sendRequest(request); - return response.body; - }, - - async list({ - token, - project_id, - filter, - }: TasksListParams): Promise { - const queryParams = { - filter, - project_id, - }; - - const request: HttpRequest = { - method: HttpMethod.GET, - url: `${API}/tasks`, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token, - }, - queryParams: pickBy(queryParams, isNotUndefined), - }; - - const response = await httpClient.sendRequest(request); - return response.body; - }, - }, + projects: { + async list({ token }: ProjectsListParams): Promise { + const request: HttpRequest = { + method: HttpMethod.GET, + url: `${API}/projects`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token, + }, + }; + + const response = await httpClient.sendRequest(request); + return response.body; + }, + }, + + sections: { + async list(params: SectionsListPrams): Promise { + const qs: Record = {}; + if (params.project_id) qs['project_id'] = params.project_id; + + const request: HttpRequest = { + method: HttpMethod.GET, + url: `${API}/sections`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: params.token, + }, + queryParams: qs, + }; + + const response = await httpClient.sendRequest(request); + return response.body; + }, + }, + + tasks: { + async create({ + token, + project_id, + content, + description, + labels, + priority, + due_date, + section_id, + }: TasksCreateParams): Promise { + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${API}/tasks`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token, + }, + body: { + content, + project_id, + description, + labels, + priority, + due_date, + section_id, + }, + }; + + const response = await httpClient.sendRequest(request); + return response.body; + }, + + async update(params: TasksUpdateParams): Promise { + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${API}/tasks/${params.task_id}`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: params.token, + }, + body: { + content: params.content, + description: params.description, + labels: params.labels?.length === 0 ? undefined : params.labels, + priority: params.priority, + due_date: params.due_date, + }, + }; + + const response = await httpClient.sendRequest(request); + return response.body; + }, + + async list({ token, project_id, filter }: TasksListParams): Promise { + const queryParams = { + filter, + project_id, + }; + + const request: HttpRequest = { + method: HttpMethod.GET, + url: `${API}/tasks`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token, + }, + queryParams: pickBy(queryParams, isNotUndefined), + }; + + const response = await httpClient.sendRequest(request); + return response.body; + }, + + async close({ token, task_id }: { token: string; task_id: string }) { + const request: HttpRequest = { + method: HttpMethod.POST, + url: `${API}/tasks/${task_id}/close`, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token, + }, + }; + + const response = await httpClient.sendRequest(request); + return response.body; + }, + }, }; type ProjectsListParams = { - token: string; + token: string; +}; + +type SectionsListPrams = { + token: string; + project_id?: string; }; type TasksCreateParams = { - token: string; + token: string; } & TodoistCreateTaskRequest; +type TasksUpdateParams = { + token: string; +} & TodoistUpdateTaskRequest; + type TasksListParams = { - token: string; - project_id?: string | undefined; - filter?: string | undefined; + token: string; + project_id?: string | undefined; + filter?: string | undefined; }; diff --git a/packages/pieces/community/todoist/src/lib/common/models.ts b/packages/pieces/community/todoist/src/lib/common/models.ts index 87c5e44cc8..c428ce1773 100644 --- a/packages/pieces/community/todoist/src/lib/common/models.ts +++ b/packages/pieces/community/todoist/src/lib/common/models.ts @@ -1,57 +1,73 @@ export type TodoistProject = { - id: string; - name: string; + id: string; + name: string; +}; + +export type TodoistSection = { + id: string; + name: string; + project_id: string; + order: number; }; export type TodoistCreateTaskRequest = { - content: string; - project_id?: string | undefined; - description?: string | undefined; - labels?: Array | undefined; - priority?: number | undefined; - due_date?: string | undefined; - section_id?: string | undefined; + content: string; + project_id?: string | undefined; + description?: string | undefined; + labels?: Array | undefined; + priority?: number | undefined; + due_date?: string | undefined; + section_id?: string | undefined; +}; + +export type TodoistUpdateTaskRequest = { + task_id: string; + content?: string; + description?: string; + labels?: Array; + priority?: number; + due_date?: string; }; type TodoistTaskDue = { - string: string; - date: string; - is_recurring: boolean; - datetime?: string | undefined; - timezone?: string | undefined; + string: string; + date: string; + is_recurring: boolean; + datetime?: string | undefined; + timezone?: string | undefined; }; export type TodoistTask = { - id: string; - projectId: string | null; - sectionId: string | null; - content: string; - description?: string | undefined; - is_completed: boolean; - labels: string[]; - parent_id: string | null; - order: number; - priority: number; - due: TodoistTaskDue | null; - url: string; - comment_count: number; - created_at: string; - creator_id: string; - assignee_id: string | null; - assigner_id: string | null; + id: string; + projectId: string | null; + sectionId: string | null; + content: string; + description?: string | undefined; + is_completed: boolean; + labels: string[]; + parent_id: string | null; + order: number; + priority: number; + due: TodoistTaskDue | null; + url: string; + comment_count: number; + created_at: string; + creator_id: string; + assignee_id: string | null; + assigner_id: string | null; }; export type TodoistCompletedTask = { - id: string; - task_id: string; - user_id: string; - project_id: string; - section_id: string; - content: string; - completed_at: string; - note_count: number; + id: string; + task_id: string; + user_id: string; + project_id: string; + section_id: string; + content: string; + completed_at: string; + note_count: number; }; export type TodoistCompletedListResponse = { - items: TodoistCompletedTask[]; + items: TodoistCompletedTask[]; }; diff --git a/packages/pieces/community/todoist/src/lib/common/props.ts b/packages/pieces/community/todoist/src/lib/common/props.ts index 94973caf6a..0fc04a559f 100644 --- a/packages/pieces/community/todoist/src/lib/common/props.ts +++ b/packages/pieces/community/todoist/src/lib/common/props.ts @@ -2,42 +2,76 @@ import { OAuth2PropertyValue, Property } from '@activepieces/pieces-framework'; import { todoistRestClient } from './client/rest-client'; const buildEmptyList = ({ placeholder }: { placeholder: string }) => { - return { - disabled: true, - options: [], - placeholder, - }; + return { + disabled: true, + options: [], + placeholder, + }; }; -export const todoistProjectIdDropdown = Property.Dropdown({ - displayName: 'Project', - refreshers: [], - description: "Task project ID. If not set, task is put to user's Inbox.", - required: false, - options: async ({ auth }) => { - if (!auth) { - return buildEmptyList({ - placeholder: 'Please select an authentication', - }); - } - - const token = (auth as OAuth2PropertyValue).access_token; - const projects = await todoistRestClient.projects.list({ token }); - - if (projects.length === 0) { - return buildEmptyList({ - placeholder: 'No projects found! Please create a project.', - }); - } - - const options = projects.map((p) => ({ - label: p.name, - value: p.id, - })); - - return { - disabled: false, - options, - }; - }, +export const todoistProjectIdDropdown = (description: string) => + Property.Dropdown({ + displayName: 'Project', + refreshers: [], + description, + required: false, + options: async ({ auth }) => { + if (!auth) { + return buildEmptyList({ + placeholder: 'Please select an authentication', + }); + } + + const token = (auth as OAuth2PropertyValue).access_token; + const projects = await todoistRestClient.projects.list({ token }); + + if (projects.length === 0) { + return buildEmptyList({ + placeholder: 'No projects found! Please create a project.', + }); + } + + const options = projects.map((p) => ({ + label: p.name, + value: p.id, + })); + + return { + disabled: false, + options, + }; + }, + }); + +export const todoistSectionIdDropdown = Property.Dropdown({ + displayName: 'Section', + refreshers: ['project_id'], + required: false, + options: async ({ auth, project_id }) => { + if (!auth) { + return buildEmptyList({ + placeholder: 'Please select an authentication', + }); + } + + const token = (auth as OAuth2PropertyValue).access_token; + const projectId = project_id as string | undefined; + const sections = await todoistRestClient.sections.list({ token, project_id: projectId }); + + if (sections.length === 0) { + return buildEmptyList({ + placeholder: 'No sections found! Please create a section.', + }); + } + + const options = sections.map((p) => ({ + label: p.name, + value: p.id, + })); + + return { + disabled: false, + options, + }; + }, }); diff --git a/packages/pieces/community/todoist/src/lib/triggers/task-completed-trigger.ts b/packages/pieces/community/todoist/src/lib/triggers/task-completed-trigger.ts index fddb9883ec..e6b058e623 100644 --- a/packages/pieces/community/todoist/src/lib/triggers/task-completed-trigger.ts +++ b/packages/pieces/community/todoist/src/lib/triggers/task-completed-trigger.ts @@ -7,7 +7,7 @@ import { todoistProjectIdDropdown } from '../common/props'; import { todoistAuth } from '../..'; type TriggerData = { - lastChecked: string; + lastChecked: string; }; const TRIGGER_DATA_STORE_KEY = 'todoist_task_completed_trigger_data'; @@ -17,60 +17,62 @@ const fiveMinutesAgo = () => dayjs().subtract(5, 'minutes').format(ISO_FORMAT); const now = () => dayjs().format(ISO_FORMAT); export const todoistTaskCompletedTrigger = createTrigger({ - auth: todoistAuth, - name: 'task_completed', - displayName: 'Task Completed', - description: 'Triggers when a new task is completed', - type: TriggerStrategy.POLLING, + auth: todoistAuth, + name: 'task_completed', + displayName: 'Task Completed', + description: 'Triggers when a new task is completed', + type: TriggerStrategy.POLLING, - sampleData: { - content: 'Buy Milk', - meta_data: null, - user_id: '2671355', - task_id: '2995104339', - note_count: 0, - project_id: '2203306141', - section_id: '7025', - completed_at: '2015-02-17T15:40:41.000000Z', - id: '1899066186', - }, + sampleData: { + content: 'Buy Milk', + meta_data: null, + user_id: '2671355', + task_id: '2995104339', + note_count: 0, + project_id: '2203306141', + section_id: '7025', + completed_at: '2015-02-17T15:40:41.000000Z', + id: '1899066186', + }, - props: { - project_id: todoistProjectIdDropdown, - }, + props: { + project_id: todoistProjectIdDropdown( + 'Leave it blank if you want to get completed tasks from all your projects.', + ), + }, - async onEnable({ store }): Promise { - await store.put(TRIGGER_DATA_STORE_KEY, { - lastChecked: now(), - }); - }, + async onEnable({ store }): Promise { + await store.put(TRIGGER_DATA_STORE_KEY, { + lastChecked: now(), + }); + }, - async onDisable({ store }): Promise { - await store.put(TRIGGER_DATA_STORE_KEY, null); - }, + async onDisable({ store }): Promise { + await store.put(TRIGGER_DATA_STORE_KEY, null); + }, - async run({ auth, propsValue, store }): Promise { - const token = auth.access_token; - const { project_id } = propsValue; + async run({ auth, propsValue, store }): Promise { + const token = auth.access_token; + const { project_id } = propsValue; - assertNotNullOrUndefined(token, 'token'); + assertNotNullOrUndefined(token, 'token'); - const triggerData = await store.get(TRIGGER_DATA_STORE_KEY); - const since = triggerData?.lastChecked ?? fiveMinutesAgo(); - const until = now(); + const triggerData = await store.get(TRIGGER_DATA_STORE_KEY); + const since = triggerData?.lastChecked ?? fiveMinutesAgo(); + const until = now(); - const response = await todoistSyncClient.completed.list({ - token, - since, - until, - project_id, - }); + const response = await todoistSyncClient.completed.list({ + token, + since, + until, + project_id, + }); - await store.put(TRIGGER_DATA_STORE_KEY, { - // It returns data newer than the since parameter, and not equal. - lastChecked: until, - }); + await store.put(TRIGGER_DATA_STORE_KEY, { + // It returns data newer than the since parameter, and not equal. + lastChecked: until, + }); - return response.items; - }, + return response.items; + }, }); From d23050c98e074bbb6feea31c64b11c012ff9bb28 Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Tue, 7 May 2024 15:00:13 +0530 Subject: [PATCH 15/50] fix(todoist): lint issue --- .../community/todoist/src/lib/actions/find-task.action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pieces/community/todoist/src/lib/actions/find-task.action.ts b/packages/pieces/community/todoist/src/lib/actions/find-task.action.ts index 168efbd65d..e7cf8a84ba 100644 --- a/packages/pieces/community/todoist/src/lib/actions/find-task.action.ts +++ b/packages/pieces/community/todoist/src/lib/actions/find-task.action.ts @@ -26,7 +26,7 @@ export const todoistFindTaskAction = createAction({ assertNotNullOrUndefined(token, 'token'); const tasks = await todoistRestClient.tasks.list({ token, project_id }); - let matchedTask = tasks.find((task) => task.content == name); + const matchedTask = tasks.find((task) => task.content == name); if (!matchedTask) { throw new Error('Task not found'); } else { From 9e3f11e62665d05db24e7a5b54da0506b67ef2a7 Mon Sep 17 00:00:00 2001 From: System Administrator Date: Wed, 8 May 2024 01:15:03 +0530 Subject: [PATCH 16/50] feat(poper): add poper integration --- .../pieces/community/poper/.eslintrc.json | 33 ++++ packages/pieces/community/poper/README.md | 7 + packages/pieces/community/poper/package.json | 4 + packages/pieces/community/poper/project.json | 38 +++++ packages/pieces/community/poper/src/index.ts | 60 ++++++++ .../poper/src/lib/triggers/new-lead.ts | 143 ++++++++++++++++++ packages/pieces/community/poper/tsconfig.json | 19 +++ .../pieces/community/poper/tsconfig.lib.json | 11 ++ tsconfig.base.json | 3 + 9 files changed, 318 insertions(+) create mode 100644 packages/pieces/community/poper/.eslintrc.json create mode 100644 packages/pieces/community/poper/README.md create mode 100644 packages/pieces/community/poper/package.json create mode 100644 packages/pieces/community/poper/project.json create mode 100644 packages/pieces/community/poper/src/index.ts create mode 100644 packages/pieces/community/poper/src/lib/triggers/new-lead.ts create mode 100644 packages/pieces/community/poper/tsconfig.json create mode 100644 packages/pieces/community/poper/tsconfig.lib.json diff --git a/packages/pieces/community/poper/.eslintrc.json b/packages/pieces/community/poper/.eslintrc.json new file mode 100644 index 0000000000..4a4e695c54 --- /dev/null +++ b/packages/pieces/community/poper/.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/poper/README.md b/packages/pieces/community/poper/README.md new file mode 100644 index 0000000000..9deb0c9627 --- /dev/null +++ b/packages/pieces/community/poper/README.md @@ -0,0 +1,7 @@ +# pieces-poper + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build pieces-poper` to build the library. diff --git a/packages/pieces/community/poper/package.json b/packages/pieces/community/poper/package.json new file mode 100644 index 0000000000..e510c65446 --- /dev/null +++ b/packages/pieces/community/poper/package.json @@ -0,0 +1,4 @@ +{ + "name": "@activepieces/piece-poper", + "version": "0.0.1" +} diff --git a/packages/pieces/community/poper/project.json b/packages/pieces/community/poper/project.json new file mode 100644 index 0000000000..89e68c6793 --- /dev/null +++ b/packages/pieces/community/poper/project.json @@ -0,0 +1,38 @@ +{ + "name": "pieces-poper", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/pieces/community/poper/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/packages/pieces/community/poper", + "tsConfig": "packages/pieces/community/poper/tsconfig.lib.json", + "packageJson": "packages/pieces/community/poper/package.json", + "main": "packages/pieces/community/poper/src/index.ts", + "assets": [ + "packages/pieces/community/poper/*.md" + ], + "buildableProjectDepsInPackageJsonType": "dependencies", + "updateBuildableProjectDepsInPackageJson": true + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs pieces-poper {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/poper/src/index.ts b/packages/pieces/community/poper/src/index.ts new file mode 100644 index 0000000000..97b55909c0 --- /dev/null +++ b/packages/pieces/community/poper/src/index.ts @@ -0,0 +1,60 @@ + + import { createPiece, PieceAuth } from "@activepieces/pieces-framework"; +import { newLead } from "./lib/triggers/new-lead"; +import { + HttpMethod, + HttpRequest, + httpClient, +} from '@activepieces/pieces-common'; + + export const poperAuth = PieceAuth.SecretText({ + displayName: 'API Key', + description: 'Use the Poper API key to authenticate the piece. You can find your API key in the Poper settings.', + required: true, + validate: async ({auth}) => { + const request: HttpRequest = { + method: HttpMethod.POST, + url: 'https://api.poper.ai/general/v1/ping', + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: { + api_key: auth, + } + }; + + try{ + + const res = await httpClient.sendRequest(request); + + if(res.status === 200){ + return { + valid: true, + } + } + + return { + valid: false, + error: 'API Key is invalid', + } + + } catch (e) { + return { + valid: false, + error: 'API Key is invalid', + } + } + + }, + }); + + export const poper = createPiece({ + displayName: "Poper", + auth: poperAuth, + minimumSupportedRelease: '0.20.0', + logoUrl: "https://cdn.activepieces.com/pieces/poper.png", + authors: [], + actions: [], + triggers: [newLead], + }); + \ No newline at end of file diff --git a/packages/pieces/community/poper/src/lib/triggers/new-lead.ts b/packages/pieces/community/poper/src/lib/triggers/new-lead.ts new file mode 100644 index 0000000000..38c6b8f9cb --- /dev/null +++ b/packages/pieces/community/poper/src/lib/triggers/new-lead.ts @@ -0,0 +1,143 @@ + +import { createTrigger, TriggerStrategy, PiecePropValueSchema, StaticPropsValue, DropdownProperty } from '@activepieces/pieces-framework'; +import { DedupeStrategy, Polling, pollingHelper, HttpMethod, HttpRequest, httpClient } from '@activepieces/pieces-common'; +import { poperAuth } from './../../'; + +import { Property } from '@activepieces/pieces-framework'; + +const buildEmptyList = ({ placeholder }: { placeholder: string }) => { + return { + disabled: true, + options: [], + placeholder, + }; +}; + +const popupsDropdown = Property.Dropdown({ + displayName: 'Popup Name', + refreshers: [], + description: "Select the popup name to trigger the action", + required: true, + options: async ({ auth }) => { + if (!auth) { + return buildEmptyList({ + placeholder: 'Please select an authentication', + }); + } + + const request: HttpRequest = { + method: HttpMethod.POST, + url: 'https://api.poper.ai/general/v1/popup/list', + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: { + api_key: auth, + } + }; + + try{ + + const res = await httpClient.sendRequest(request); + + if(res.status === 200){ + const popups = res.body.popups; + if (popups.length === 0) { + return buildEmptyList({ + placeholder: 'No popups found! Please create a popup.', + }); + } + + const options = popups.map((p:any) => ({ + label: p.name, + value: p.id, + })); + + return { + disabled: false, + options, + }; + } + + return buildEmptyList({ + placeholder: 'Not authorized.', + }); + } catch(e){ + return buildEmptyList({ + placeholder: 'Not authorized.', + }); + } + + }, +}); + +// replace auth with piece auth variable +const polling: Polling< PiecePropValueSchema, StaticPropsValue<{ popup_id: DropdownProperty | DropdownProperty; }> > = { + strategy: DedupeStrategy.LAST_ITEM, + items: async ({ auth, propsValue }) => { + // implement the logic to fetch the items + const request: HttpRequest = { + method: HttpMethod.POST, + url: 'https://api.poper.ai/general/v1/popup/responses', + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: { + api_key: auth, + popup_id: propsValue.popup_id, + } + }; + + try{ + + const res = await httpClient.sendRequest(request); + + if(res.status === 200){ + const responses = res.body.responses; + if (responses.length === 0) { + return []; + } + + return responses.map((response:any) => ({ + id: response.id, + data: response, + })); + } + + return []; + } catch(e){ + return []; + } + }, + +} + +export const newLead = createTrigger({ +auth: poperAuth, +name: 'newLead', +displayName: 'New Lead', +description: 'Triggers when a new lead is obtained from popup', +props: { + popup_id: popupsDropdown, +}, +sampleData: {}, +type: TriggerStrategy.POLLING, +async test(context) { + const { store, auth, propsValue } = context; + return await pollingHelper.test(polling, { store, auth, propsValue }); +}, +async onEnable(context) { + const { store, auth, propsValue } = context; + await pollingHelper.onEnable(polling, { store, auth, propsValue }); +}, + +async onDisable(context) { + const { store, auth, propsValue } = context; + await pollingHelper.onDisable(polling, { store, auth, propsValue }); +}, + +async run(context) { + const { store, auth, propsValue } = context; + return await pollingHelper.poll(polling, { store, auth, propsValue }); +}, +}); \ No newline at end of file diff --git a/packages/pieces/community/poper/tsconfig.json b/packages/pieces/community/poper/tsconfig.json new file mode 100644 index 0000000000..059cd81661 --- /dev/null +++ b/packages/pieces/community/poper/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/poper/tsconfig.lib.json b/packages/pieces/community/poper/tsconfig.lib.json new file mode 100644 index 0000000000..28369ef762 --- /dev/null +++ b/packages/pieces/community/poper/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/tsconfig.base.json b/tsconfig.base.json index acf980862d..9f816691fc 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -390,6 +390,9 @@ "@activepieces/piece-pipedrive": [ "packages/pieces/community/pipedrive/src/index.ts" ], + "@activepieces/piece-poper": [ + "packages/pieces/community/poper/src/index.ts" + ], "@activepieces/piece-postgres": [ "packages/pieces/community/postgres/src/index.ts" ], From 00cdf0d36d830f546d3c81dc78346fa00705c989 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 8 May 2024 02:13:09 +0000 Subject: [PATCH 17/50] fix: package.json to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-PDFJSDIST-6810403 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 90a068826f..dda3b1bb62 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,7 @@ "openai": "4.17.5", "papaparse": "5.4.1", "pdf-parse": "1.1.1", - "pdf-text-reader": "4.0.1", + "pdf-text-reader": "5.0.0", "pg": "8.11.3", "pickleparser": "0.1.0", "pino-loki": "2.1.3", From 0b301ae738172c8b729b7132b7b575c869b5b93a Mon Sep 17 00:00:00 2001 From: System Administrator Date: Wed, 8 May 2024 09:09:29 +0530 Subject: [PATCH 18/50] feat(poper): bug fix --- packages/pieces/community/poper/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pieces/community/poper/src/index.ts b/packages/pieces/community/poper/src/index.ts index 97b55909c0..22b4ae85c0 100644 --- a/packages/pieces/community/poper/src/index.ts +++ b/packages/pieces/community/poper/src/index.ts @@ -52,7 +52,7 @@ import { displayName: "Poper", auth: poperAuth, minimumSupportedRelease: '0.20.0', - logoUrl: "https://cdn.activepieces.com/pieces/poper.png", + logoUrl: "https://cdn.poper.ai/poper-logo.png", authors: [], actions: [], triggers: [newLead], From 4512c9beed8b90183c4a35e733999570b29058bd Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Wed, 8 May 2024 11:50:17 +0530 Subject: [PATCH 19/50] fix(github): add pagination for repo dropdown --- packages/pieces/community/github/package.json | 2 +- .../community/github/src/lib/common/index.ts | 302 +++++++++--------- 2 files changed, 157 insertions(+), 147 deletions(-) diff --git a/packages/pieces/community/github/package.json b/packages/pieces/community/github/package.json index 2970bc75dd..9d25f1fc13 100644 --- a/packages/pieces/community/github/package.json +++ b/packages/pieces/community/github/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-github", - "version": "0.3.9" + "version": "0.3.10" } diff --git a/packages/pieces/community/github/src/lib/common/index.ts b/packages/pieces/community/github/src/lib/common/index.ts index 3cfeb9f92b..51eae540ce 100644 --- a/packages/pieces/community/github/src/lib/common/index.ts +++ b/packages/pieces/community/github/src/lib/common/index.ts @@ -1,170 +1,180 @@ import { Property, OAuth2PropertyValue } from '@activepieces/pieces-framework'; import { - HttpRequest, - HttpMethod, - AuthenticationType, - httpClient, + HttpRequest, + HttpMethod, + AuthenticationType, + httpClient, } from '@activepieces/pieces-common'; export const githubCommon = { - baseUrl: 'https://api.github.com', - repositoryDropdown: Property.Dropdown<{ repo: string; owner: string }>({ - displayName: 'Repository', - refreshers: [], - required: true, - options: async ({ auth }) => { - if (!auth) { - return { - disabled: true, - options: [], - placeholder: 'please authenticate first', - }; - } - const authProp: OAuth2PropertyValue = auth as OAuth2PropertyValue; - const repositories = await getUserRepo(authProp); - return { - disabled: false, - options: repositories.map((repo) => { - return { - label: repo.owner.login + '/' + repo.name, - value: { - owner: repo.owner.login, - repo: repo.name, - }, - }; - }), - }; - }, - }), - assigneeDropDown: (required = false) => - Property.MultiSelectDropdown({ - displayName: 'Assignees', - description: 'Assignees for the Issue', - refreshers: ['repository'], + baseUrl: 'https://api.github.com', + repositoryDropdown: Property.Dropdown<{ repo: string; owner: string }>({ + displayName: 'Repository', + refreshers: [], + required: true, + options: async ({ auth }) => { + if (!auth) { + return { + disabled: true, + options: [], + placeholder: 'please authenticate first', + }; + } + const authProp: OAuth2PropertyValue = auth as OAuth2PropertyValue; + const repositories = await getUserRepo(authProp); + return { + disabled: false, + options: repositories.map((repo) => { + return { + label: repo.owner.login + '/' + repo.name, + value: { + owner: repo.owner.login, + repo: repo.name, + }, + }; + }), + }; + }, + }), + assigneeDropDown: (required = false) => + Property.MultiSelectDropdown({ + displayName: 'Assignees', + description: 'Assignees for the Issue', + refreshers: ['repository'], - required, - options: async ({ auth, repository }) => { - if (!auth || !repository) { - return { - disabled: true, - options: [], - placeholder: 'please authenticate first and select repo', - }; - } - const authProp: OAuth2PropertyValue = auth as OAuth2PropertyValue; - const { owner, repo } = repository as RepositoryProp; - const assignees = await getAssignee(authProp, owner, repo); - return { - disabled: false, - options: assignees.map((assignee) => { - return { - label: assignee.login, - value: assignee.login, - }; - }), - }; - }, - }), - labelDropDown: (required = false) => - Property.MultiSelectDropdown({ - displayName: 'Labels', - description: 'Labels for the Issue', - refreshers: ['repository'], - required, - options: async ({ auth, repository }) => { - if (!auth || !repository) { - return { - disabled: true, - options: [], - placeholder: 'please authenticate first and select repo', - }; - } - const authProp: OAuth2PropertyValue = auth as OAuth2PropertyValue; - const { owner, repo } = repository as RepositoryProp; - const labels = await listIssueLabels(authProp, owner, repo); - return { - disabled: false, - options: labels.map((label) => { - return { - label: label.name, - value: label.name, - }; - }), - }; - }, - }), + required, + options: async ({ auth, repository }) => { + if (!auth || !repository) { + return { + disabled: true, + options: [], + placeholder: 'please authenticate first and select repo', + }; + } + const authProp: OAuth2PropertyValue = auth as OAuth2PropertyValue; + const { owner, repo } = repository as RepositoryProp; + const assignees = await getAssignee(authProp, owner, repo); + return { + disabled: false, + options: assignees.map((assignee) => { + return { + label: assignee.login, + value: assignee.login, + }; + }), + }; + }, + }), + labelDropDown: (required = false) => + Property.MultiSelectDropdown({ + displayName: 'Labels', + description: 'Labels for the Issue', + refreshers: ['repository'], + required, + options: async ({ auth, repository }) => { + if (!auth || !repository) { + return { + disabled: true, + options: [], + placeholder: 'please authenticate first and select repo', + }; + } + const authProp: OAuth2PropertyValue = auth as OAuth2PropertyValue; + const { owner, repo } = repository as RepositoryProp; + const labels = await listIssueLabels(authProp, owner, repo); + return { + disabled: false, + options: labels.map((label) => { + return { + label: label.name, + value: label.name, + }; + }), + }; + }, + }), }; -async function getUserRepo( - authProp: OAuth2PropertyValue -): Promise { - const request: HttpRequest = { - method: HttpMethod.GET, - url: `${githubCommon.baseUrl}/user/repos`, - queryParams: { - per_page: '200', - }, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: authProp.access_token, - }, - }; - const response = await httpClient.sendRequest(request); - return response.body; +async function getUserRepo(authProp: OAuth2PropertyValue): Promise { + let page = 1; + let hasNext; + const repos: GithubRepository[] = []; + do { + const request: HttpRequest = { + method: HttpMethod.GET, + url: `${githubCommon.baseUrl}/user/repos`, + queryParams: { + page: page.toString(), + per_page: '100', + }, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: authProp.access_token, + }, + }; + + const response = await httpClient.sendRequest(request); + repos.push(...response.body); + + hasNext = response.headers?.link?.includes('rel="next"'); + page += 1; + } while (hasNext); + + return repos; } async function getAssignee( - authProp: OAuth2PropertyValue, - owner: string, - repo: string + authProp: OAuth2PropertyValue, + owner: string, + repo: string, ): Promise { - const request: HttpRequest = { - method: HttpMethod.GET, - url: `${githubCommon.baseUrl}/repos/${owner}/${repo}/assignees`, - queryParams: { - per_page: '30', - }, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: authProp.access_token, - }, - }; - const response = await httpClient.sendRequest(request); - return response.body; + const request: HttpRequest = { + method: HttpMethod.GET, + url: `${githubCommon.baseUrl}/repos/${owner}/${repo}/assignees`, + queryParams: { + per_page: '30', + }, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: authProp.access_token, + }, + }; + const response = await httpClient.sendRequest(request); + return response.body; } async function listIssueLabels( - authProp: OAuth2PropertyValue, - owner: string, - repo: string + authProp: OAuth2PropertyValue, + owner: string, + repo: string, ): Promise { - const request: HttpRequest = { - method: HttpMethod.GET, - url: `${githubCommon.baseUrl}/repos/${owner}/${repo}/labels`, - queryParams: { - per_page: '30', - }, - authentication: { - type: AuthenticationType.BEARER_TOKEN, - token: authProp.access_token, - }, - }; - const response = await httpClient.sendRequest(request); - return response.body; + const request: HttpRequest = { + method: HttpMethod.GET, + url: `${githubCommon.baseUrl}/repos/${owner}/${repo}/labels`, + queryParams: { + per_page: '30', + }, + authentication: { + type: AuthenticationType.BEARER_TOKEN, + token: authProp.access_token, + }, + }; + const response = await httpClient.sendRequest(request); + return response.body; } export interface GithubRepository { - name: string; - owner: { - login: string; - }; + name: string; + owner: { + login: string; + }; } export interface GithubAssignee { - login: string; + login: string; } export interface GithubIssueLabel { - id: string; - name: string; + id: string; + name: string; } export interface RepositoryProp { - repo: string; - owner: string; + repo: string; + owner: string; } From 8e9487cb6992031947123f419396e5e8dfbbcc05 Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Wed, 8 May 2024 12:15:08 +0530 Subject: [PATCH 20/50] chore: add logo url,piece category and desc --- packages/pieces/community/poper/src/index.ts | 106 +++++++++---------- 1 file changed, 51 insertions(+), 55 deletions(-) diff --git a/packages/pieces/community/poper/src/index.ts b/packages/pieces/community/poper/src/index.ts index 22b4ae85c0..b70c9b5ee3 100644 --- a/packages/pieces/community/poper/src/index.ts +++ b/packages/pieces/community/poper/src/index.ts @@ -1,60 +1,56 @@ +import { createPiece, PieceAuth } from '@activepieces/pieces-framework'; +import { newLead } from './lib/triggers/new-lead'; +import { HttpMethod, HttpRequest, httpClient } from '@activepieces/pieces-common'; +import { PieceCategory } from '@activepieces/shared'; - import { createPiece, PieceAuth } from "@activepieces/pieces-framework"; -import { newLead } from "./lib/triggers/new-lead"; -import { - HttpMethod, - HttpRequest, - httpClient, -} from '@activepieces/pieces-common'; - - export const poperAuth = PieceAuth.SecretText({ - displayName: 'API Key', - description: 'Use the Poper API key to authenticate the piece. You can find your API key in the Poper settings.', - required: true, - validate: async ({auth}) => { - const request: HttpRequest = { - method: HttpMethod.POST, - url: 'https://api.poper.ai/general/v1/ping', - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: { - api_key: auth, - } - }; +export const poperAuth = PieceAuth.SecretText({ + displayName: 'API Key', + description: + 'Use the Poper API key to authenticate the piece. You can find your API key in the Poper settings.', + required: true, + validate: async ({ auth }) => { + const request: HttpRequest = { + method: HttpMethod.POST, + url: 'https://api.poper.ai/general/v1/ping', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: { + api_key: auth, + }, + }; - try{ - - const res = await httpClient.sendRequest(request); + try { + const res = await httpClient.sendRequest(request); - if(res.status === 200){ - return { - valid: true, - } - } + if (res.status === 200) { + return { + valid: true, + }; + } - return { - valid: false, - error: 'API Key is invalid', - } + return { + valid: false, + error: 'API Key is invalid', + }; + } catch (e) { + return { + valid: false, + error: 'API Key is invalid', + }; + } + }, +}); - } catch (e) { - return { - valid: false, - error: 'API Key is invalid', - } - } - - }, - }); - - export const poper = createPiece({ - displayName: "Poper", - auth: poperAuth, - minimumSupportedRelease: '0.20.0', - logoUrl: "https://cdn.poper.ai/poper-logo.png", - authors: [], - actions: [], - triggers: [newLead], - }); - \ No newline at end of file +export const poper = createPiece({ + displayName: 'Poper', + auth: poperAuth, + minimumSupportedRelease: '0.20.0', + categories: [PieceCategory.MARKETING], + description: + 'AI Driven Pop-up Builder that can convert visitors into customers,increase subscriber count, and skyrocket sales.', + logoUrl: 'https://cdn.activepieces.com/pieces/poper.png', + authors: ['thirstycode'], + actions: [], + triggers: [newLead], +}); From 9ee2fd51d02707dcd9251d08a19a65b546a74005 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 06:49:12 +0000 Subject: [PATCH 21/50] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2a1b94b277..8b9e159c81 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,7 @@ Not into coding but still interested in contributing? Come join our [Discord](ht
+
Javier HM
Javier HM

🔌
Mohamed Hassan
Mohamed Hassan

🐛
Christian Schab
Christian Schab

🔌
Pratik Kinage
Pratik Kinage

🔌
From 39e9af0728fb18aa613778374843c6ef694c9dad Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 06:49:13 +0000 Subject: [PATCH 22/50] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 1d38a9036a..09effa6a78 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1010,6 +1010,15 @@ "contributions": [ "plugin" ] + }, + { + "login": "thirstycode", + "name": "Pratik Kinage", + "avatar_url": "https://avatars.githubusercontent.com/u/37847256?v=4", + "profile": "https://www.gamespecifications.com/", + "contributions": [ + "plugin" + ] } ], "commitType": "docs" From c798fc5f3f47a7805a577d1912e4eec7bf5d9cd4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 06:58:30 +0000 Subject: [PATCH 23/50] chore(deps): bump pdfjs-dist and pdf-text-reader Bumps [pdfjs-dist](https://github.com/mozilla/pdfjs-dist) to 4.2.67 and updates ancestor dependency [pdf-text-reader](https://github.com/electrovir/pdf-text-reader). These dependencies need to be updated together. Updates `pdfjs-dist` from 3.9.179 to 4.2.67 - [Commits](https://github.com/mozilla/pdfjs-dist/commits) Updates `pdf-text-reader` from 4.0.1 to 5.0.0 - [Release notes](https://github.com/electrovir/pdf-text-reader/releases) - [Commits](https://github.com/electrovir/pdf-text-reader/compare/v4.0.1...v5.0.0) --- updated-dependencies: - dependency-name: pdfjs-dist dependency-type: indirect - dependency-name: pdf-text-reader dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 36 ++++++++++++------------------------ package.json | 2 +- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 754d9f4884..1778664370 100644 --- a/package-lock.json +++ b/package-lock.json @@ -147,7 +147,7 @@ "openai": "4.17.5", "papaparse": "5.4.1", "pdf-parse": "1.1.1", - "pdf-text-reader": "4.0.1", + "pdf-text-reader": "5.0.0", "pg": "8.11.3", "pickleparser": "0.1.0", "pino-loki": "2.1.3", @@ -34474,26 +34474,14 @@ } }, "node_modules/path2d": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.1.1.tgz", - "integrity": "sha512-/+S03c8AGsDYKKBtRDqieTJv2GlkMb0bWjnqOgtF6MkjdUQ9a8ARAtxWf9NgKLGm2+WQr6+/tqJdU8HNGsIDoA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.0.tgz", + "integrity": "sha512-KdPAykQX6kmLSOO6Jpu2KNcCED7CKjmaBNGGNuctOsG0hgYO1OdYQaan6cYXJiG0WmXOwZZPILPBimu5QAIw3A==", "optional": true, "engines": { "node": ">=6" } }, - "node_modules/path2d-polyfill": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.1.1.tgz", - "integrity": "sha512-4Rka5lN+rY/p0CdD8+E+BFv51lFaFvJOrlOhyQ+zjzyQrzyh3ozmxd1vVGGDdIbUFSBtIZLSnspxTgPT0iJhvA==", - "optional": true, - "dependencies": { - "path2d": "0.1.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -34524,23 +34512,23 @@ } }, "node_modules/pdf-text-reader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pdf-text-reader/-/pdf-text-reader-4.0.1.tgz", - "integrity": "sha512-AuXTpAnZORgq6c1CblRZ6j+X6GTV1ap8iDmOd4koefXwCzqdzJjmRkXUVpuAxHdHjYji2j4kN98vJvK5PJE3PA==", + "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==", "dependencies": { - "pdfjs-dist": "3.9.179" + "pdfjs-dist": "4.2.67" } }, "node_modules/pdfjs-dist": { - "version": "3.9.179", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.9.179.tgz", - "integrity": "sha512-AZBEIAORYDaOAlM0/A4Zg465+XF3ugYDdgrVmioVvNW5tH3xs3RpGFBYOG5PM9/vLM3M/wNncsMLTgyIKdqMKg==", + "version": "4.2.67", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.2.67.tgz", + "integrity": "sha512-rJmuBDFpD7cqC8WIkQUEClyB4UAH05K4AsyewToMTp2gSy3Rrx8c1ydAVqlJlGv3yZSOrhEERQU/4ScQQFlLHA==", "engines": { "node": ">=18" }, "optionalDependencies": { "canvas": "^2.11.2", - "path2d-polyfill": "^2.0.1" + "path2d": "^0.2.0" } }, "node_modules/peberminta": { diff --git a/package.json b/package.json index 90a068826f..dda3b1bb62 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,7 @@ "openai": "4.17.5", "papaparse": "5.4.1", "pdf-parse": "1.1.1", - "pdf-text-reader": "4.0.1", + "pdf-text-reader": "5.0.0", "pg": "8.11.3", "pickleparser": "0.1.0", "pino-loki": "2.1.3", From 8a8347790c46dd1ecafa23db951b2c93e7ae8783 Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Wed, 8 May 2024 14:44:14 +0530 Subject: [PATCH 24/50] fix(trello): fix boards dropdown --- .../pieces/community/trello/src/lib/actions/get-card.ts | 2 -- packages/pieces/community/trello/src/lib/common/index.ts | 6 ------ 2 files changed, 8 deletions(-) diff --git a/packages/pieces/community/trello/src/lib/actions/get-card.ts b/packages/pieces/community/trello/src/lib/actions/get-card.ts index 62a173b6d9..10980e9612 100644 --- a/packages/pieces/community/trello/src/lib/actions/get-card.ts +++ b/packages/pieces/community/trello/src/lib/actions/get-card.ts @@ -34,8 +34,6 @@ export const getCard = createAction({ headers: { Accept: 'application/json', }, - body: {}, - queryParams: {}, }; return (await httpClient.sendRequest(request)).body; }, diff --git a/packages/pieces/community/trello/src/lib/common/index.ts b/packages/pieces/community/trello/src/lib/common/index.ts index af1e83b37a..3316d0c7be 100644 --- a/packages/pieces/community/trello/src/lib/common/index.ts +++ b/packages/pieces/community/trello/src/lib/common/index.ts @@ -184,8 +184,6 @@ async function getAuthorisedUser(apikey: string, token: string) { headers: { Accept: 'application/json', }, - body: {}, - queryParams: {}, }; const response = await httpClient.sendRequest(request); @@ -211,8 +209,6 @@ async function listBoards(apikey: string, token: string, user_id: string) { headers: { Accept: 'application/json', }, - body: {}, - queryParams: {}, }; const response = await httpClient.sendRequest<{ id: string; name: string }[]>( request @@ -240,8 +236,6 @@ async function listBoardLists(apikey: string, token: string, board_id: string) { headers: { Accept: 'application/json', }, - body: {}, - queryParams: {}, }; const response = await httpClient.sendRequest<{ id: string; name: string }[]>( request From b084af591a8a318313b978432be701a2b9a3d92a Mon Sep 17 00:00:00 2001 From: Kishan Parmar Date: Wed, 8 May 2024 14:44:50 +0530 Subject: [PATCH 25/50] chore: bump trello version --- packages/pieces/community/trello/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pieces/community/trello/package.json b/packages/pieces/community/trello/package.json index 3d2166e726..6450e725bd 100644 --- a/packages/pieces/community/trello/package.json +++ b/packages/pieces/community/trello/package.json @@ -1,4 +1,4 @@ { "name": "@activepieces/piece-trello", - "version": "0.3.5" + "version": "0.3.6" } \ No newline at end of file From 741115c490cf7cd4a395b7df1753ddffc9f96cf2 Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Wed, 8 May 2024 18:23:32 +0000 Subject: [PATCH 26/50] fix: continue flow execution in UI when resuming --- .../app/flows/flow-run/flow-run-service.ts | 21 ++++++++++++++++++- .../server/api/src/app/flows/flow.module.ts | 15 ++++++++++--- .../flow-worker/engine-response-watcher.ts | 3 +++ .../lib/flow-run/execution/flow-execution.ts | 1 + 4 files changed, 36 insertions(+), 4 deletions(-) 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..2c796f3249 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,20 @@ 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 + } +} + export const flowRunService = { async list({ projectId, @@ -173,6 +187,8 @@ export const flowRunService = { flowRunId: flowRunToResume.id, projectId: flowRunToResume.projectId, flowVersionId: flowRunToResume.flowVersionId, + synchronousHandlerId: returnHandlerId(pauseMetadata, requestId), + hookType: HookType.BEFORE_LOG, executionType, environment: RunEnvironment.PRODUCTION, }) @@ -288,11 +304,14 @@ export const flowRunService = { const { flowRunId, logFileId, pauseMetadata } = params + const updatedPauseMetadata = params.pauseMetadata.type === PauseType.WEBHOOK ? { ...pauseMetadata, + id: engineResponseWatcher.getHandlerId() } : pauseMetadata + await flowRunRepo.update(flowRunId, { status: FlowRunStatus.PAUSED, logsFileId: logFileId, // eslint-disable-next-line @typescript-eslint/no-explicit-any - pauseMetadata: pauseMetadata as any, + pauseMetadata: updatedPauseMetadata 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 d913879d9b..e35b0b503a 100644 --- a/packages/server/api/src/app/flows/flow.module.ts +++ b/packages/server/api/src/app/flows/flow.module.ts @@ -9,7 +9,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, FlowRunStatus, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' export const flowModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(flowVersionController, { prefix: '/v1/flows' }) @@ -24,8 +24,17 @@ export const flowModule: FastifyPluginAsyncTypebox = async (app) => { 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) + + let finalFlowRun = flowRun + while (finalFlowRun.status === FlowRunStatus.PAUSED) { + await engineResponseWatcher.listen(finalFlowRun.id, false) + finalFlowRun = await flowRunService.getOneOrThrow({ id: finalFlowRun.id, projectId: principal.projectId }) + socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_FINISHED, finalFlowRun) + } + + if (finalFlowRun.status === FlowRunStatus.SUCCEEDED) { + engineResponseWatcher.removeListener(flowRun.id) + } } }) websocketService.addListener(WebsocketServerEvent.TEST_STEP_RUN, (socket) => { 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..90b7f7ba71 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 @@ -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') 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 From b85c89c5460303681553251144754bdadb3a5339 Mon Sep 17 00:00:00 2001 From: Mohammed Abu Aboud Date: Thu, 9 May 2024 00:07:39 +0200 Subject: [PATCH 27/50] chore: update package.json --- packages/pieces/community/webhook/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pieces/community/webhook/package.json b/packages/pieces/community/webhook/package.json index 675fda0e6c..fca6daff4f 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.2.2" -} \ No newline at end of file + "version": "0.2.0" +} From d3449306ea01897c60ee287da7caa8421cadf454 Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Wed, 8 May 2024 22:16:12 +0000 Subject: [PATCH 28/50] fix: webhook url --- .../pieces/community/webhook/src/lib/triggers/catch-hook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 42e0b26f5a..a4a4128d0c 100644 --- a/packages/pieces/community/webhook/src/lib/triggers/catch-hook.ts +++ b/packages/pieces/community/webhook/src/lib/triggers/catch-hook.ts @@ -17,7 +17,7 @@ const message = ` \`\`\`text {{webhookUrl}}/test \`\`\` -***This URL can be used to test the webhook without triggering the flow.*** +***Use this URL for testing the webhook and saving sample data. It won't start the flow***. **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. From 22d0ae1303f46d543cee428c1bd296795ea94a7c Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Wed, 8 May 2024 22:25:39 +0000 Subject: [PATCH 29/50] fix: openai extract structured data --- .../actions/extract-structure-data.action.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) 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 7abf87983f..eddd8d59e0 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 @@ -5,8 +5,8 @@ import { notLLMs } from '../common/common'; export const extractStructuredDataAction = createAction({ auth: openaiAuth, - name: 'openai-extract-structured-data', - displayName: 'Transform Text to Structured Data', + name: 'extract-structured-data', + displayName: 'Extract Structured Data from Text', description: 'Returns structured data from provided unstructured text.', props: { model: Property.Dropdown({ @@ -71,7 +71,7 @@ export const extractStructuredDataAction = createAction({ displayName: 'Description', description: 'Brief description of the parameter, defining what data will be extracted to this parameter.', - required: true, + required: false, }), propDataType: Property.StaticDropdown({ displayName: 'Data Type', @@ -81,14 +81,15 @@ export const extractStructuredDataAction = createAction({ options: { disabled: false, options: [ - { label: 'string', value: 'string' }, - { label: 'number', value: 'number' }, - { label: 'boolean', value: 'boolean' }, + { label: 'Text', value: 'string' }, + { label: 'Number', value: 'number' }, + { label: 'Boolean', value: 'boolean' }, ], }, }), propIsRequired: Property.Checkbox({ - displayName: 'Is Parameter Required?', + displayName: 'Is Property Required?', + description: 'If the property must be present, the action will fail if it is not found.', required: true, defaultValue: true, }), @@ -97,17 +98,17 @@ export const extractStructuredDataAction = createAction({ }, async run(context) { const { model, text, prompt } = context.propsValue; - const paramInputArray = context.propsValue.params as ParamInput[]; - - const functionParams: Record = {}; - const requiredFunctionParams = []; + const functionParams: Record = {}; + const requiredFunctionParams: string[] = []; for (const param of paramInputArray) { functionParams[param.propName] = { type: param.propDataType, description: param.propDescription, }; - if (param.propIsRequired) requiredFunctionParams.push(param.propName); + if (param.propIsRequired) { + requiredFunctionParams.push(param.propName); + } } const openai = new OpenAI({ @@ -137,7 +138,9 @@ export const extractStructuredDataAction = createAction({ if (toolCallsResponse) { return JSON.parse(toolCallsResponse[0].function.arguments); } else { - throw Error('Unable to extract data. Please provide valid params and text.'); + throw Error(JSON.stringify({ + message: 'Unable to extract data. Please provide valid params and text.' + })); } }, }); From c2af8168222bba904318230979288fcaef183f6b Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Thu, 9 May 2024 00:08:18 +0000 Subject: [PATCH 30/50] feat: emit events with driver function --- .../server/api/src/app/flows/flow.module.ts | 25 ++++++++++--------- .../flow-worker/engine-response-watcher.ts | 16 +++++++++++- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/server/api/src/app/flows/flow.module.ts b/packages/server/api/src/app/flows/flow.module.ts index e35b0b503a..ecd100c854 100644 --- a/packages/server/api/src/app/flows/flow.module.ts +++ b/packages/server/api/src/app/flows/flow.module.ts @@ -1,3 +1,4 @@ +import EventEmitter from 'events' import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' import { accessTokenManager } from '../authentication/lib/access-token-manager' import { websocketService } from '../websockets/websockets.service' @@ -9,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, FlowRunStatus, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' +import { CreateStepRunRequestBody, FlowRun, FlowRunStatus, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' export const flowModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(flowVersionController, { prefix: '/v1/flows' }) @@ -18,23 +19,23 @@ export const flowModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(testTriggerController, { prefix: '/v1/test-trigger' }) websocketService.addListener(WebsocketServerEvent.TEST_FLOW_RUN, (socket) => { return async (data: TestFlowRunRequestBody) => { + const eventEmitter = new EventEmitter() const principal = await accessTokenManager.extractPrincipal(socket.handshake.auth.token) const flowRun = await flowRunService.test({ projectId: principal.projectId, flowVersionId: data.flowVersionId, }) - socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_STARTED, flowRun) - let finalFlowRun = flowRun - while (finalFlowRun.status === FlowRunStatus.PAUSED) { - await engineResponseWatcher.listen(finalFlowRun.id, false) - finalFlowRun = await flowRunService.getOneOrThrow({ id: finalFlowRun.id, projectId: principal.projectId }) - socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_FINISHED, finalFlowRun) - } - - if (finalFlowRun.status === FlowRunStatus.SUCCEEDED) { - engineResponseWatcher.removeListener(flowRun.id) - } + socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_STARTED, flowRun) + + eventEmitter.on('FlowStatus', (flowRunResponse: FlowRun) => { + if (flowRunResponse.status === FlowRunStatus.SUCCEEDED) { + eventEmitter.removeAllListeners() + } + socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_FINISHED, flowRunResponse) + }) + + await engineResponseWatcher.listenAndEmit(flowRun.id, eventEmitter, flowRunService.getOneOrThrow({ id: flowRun.id, projectId: principal.projectId })) } }) websocketService.addListener(WebsocketServerEvent.TEST_STEP_RUN, (socket) => { 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 90b7f7ba71..285e489a0f 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,8 +1,9 @@ +import { EventEmitter } from 'events' import { logger } from '@sentry/utils' import { StatusCodes } from 'http-status-codes' import { pubSub } from '../../helper/pubsub' import { system, SystemProp } from '@activepieces/server-shared' -import { apId } from '@activepieces/shared' +import { apId, FlowRunStatus } from '@activepieces/shared' const listeners = new Map void>() @@ -45,6 +46,19 @@ export const engineResponseWatcher = { }, ) }, + async listenAndEmit(requestId: string, event: EventEmitter, driver: Promise): Promise { + logger.info(`[engineWatcher#listenAndEmit] requestId=${requestId}`) + + const listenStatus = async () => { + const finalFlowRun = await driver + event.emit('FlowStatus', finalFlowRun) + if (finalFlowRun.status !== FlowRunStatus.SUCCEEDED) { + await listenStatus() + } + } + + await listenStatus() + }, async listen(requestId: string, timeoutRequest: boolean): Promise { logger.info(`[engineWatcher#listen] requestId=${requestId}`) return new Promise((resolve) => { From e92f370589b4c39327252016d33d476d90abc923 Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Thu, 9 May 2024 00:11:59 +0000 Subject: [PATCH 31/50] fix: remove all listeners when failed --- packages/server/api/src/app/flows/flow.module.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/api/src/app/flows/flow.module.ts b/packages/server/api/src/app/flows/flow.module.ts index ecd100c854..48d5ae8cb3 100644 --- a/packages/server/api/src/app/flows/flow.module.ts +++ b/packages/server/api/src/app/flows/flow.module.ts @@ -29,8 +29,9 @@ export const flowModule: FastifyPluginAsyncTypebox = async (app) => { socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_STARTED, flowRun) eventEmitter.on('FlowStatus', (flowRunResponse: FlowRun) => { - if (flowRunResponse.status === FlowRunStatus.SUCCEEDED) { + if (flowRunResponse.status === FlowRunStatus.SUCCEEDED || flowRunResponse.status === FlowRunStatus.FAILED) { eventEmitter.removeAllListeners() + engineResponseWatcher.removeListener(flowRun.id) } socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_FINISHED, flowRunResponse) }) From 4966031640ae258c9419351fb938c8ba4b54bf78 Mon Sep 17 00:00:00 2001 From: Mohammed Abu Aboud Date: Thu, 9 May 2024 12:23:08 +0200 Subject: [PATCH 32/50] chore: change package.json in webhook --- packages/pieces/community/webhook/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pieces/community/webhook/package.json b/packages/pieces/community/webhook/package.json index fca6daff4f..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.2.0" + "version": "0.1.3" } From 515e7078e7126522826b9a5f80c42385e1f6f0b4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 10:28:04 +0000 Subject: [PATCH 33/50] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8b9e159c81..9ed6d1fbaf 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,7 @@ 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

🔌 From cd247c99492ae96a43e6ce29607e1f0092036744 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 10:28:05 +0000 Subject: [PATCH 34/50] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 09effa6a78..59c42ce562 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1019,6 +1019,15 @@ "contributions": [ "plugin" ] + }, + { + "login": "LevwTech", + "name": "Abdelrahman Mostafa ", + "avatar_url": "https://avatars.githubusercontent.com/u/69399787?v=4", + "profile": "https://github.com/LevwTech", + "contributions": [ + "plugin" + ] } ], "commitType": "docs" From 90ed969da70da30fb268376731f85d8ac6de496b Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Thu, 9 May 2024 10:35:21 +0000 Subject: [PATCH 35/50] chore: rename piece --- packages/pieces/community/whatsapp/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pieces/community/whatsapp/src/index.ts b/packages/pieces/community/whatsapp/src/index.ts index e9a7a14a1b..a856dea7e3 100644 --- a/packages/pieces/community/whatsapp/src/index.ts +++ b/packages/pieces/community/whatsapp/src/index.ts @@ -36,7 +36,7 @@ export const whatsappAuth = PieceAuth.CustomAuth({ export const whatsapp = createPiece({ - displayName: "WhatsApp", + displayName: "WhatsApp Business", description: 'Manage your WhatsApp business account', auth: whatsappAuth, minimumSupportedRelease: '0.20.0', From 8e7bff8b3e4bf0f07d70dc3a32ebb862eae9c690 Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Thu, 9 May 2024 12:01:08 +0000 Subject: [PATCH 36/50] fix: make pauseMetadata handler as a separate function --- .../src/app/flows/flow-run/flow-run-service.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) 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 2c796f3249..392204a838 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 @@ -92,6 +92,17 @@ function returnHandlerId(pauseMetadata: PauseMetadata | undefined, requestId: st } } +function modifyPauseMetadata(pauseMetadata: PauseMetadata): PauseMetadata { + if (pauseMetadata.type === PauseType.WEBHOOK) { + return { + ...pauseMetadata, + handlerId: engineResponseWatcher.getHandlerId(), + } + } + + return pauseMetadata +} + export const flowRunService = { async list({ projectId, @@ -304,14 +315,11 @@ export const flowRunService = { const { flowRunId, logFileId, pauseMetadata } = params - const updatedPauseMetadata = params.pauseMetadata.type === PauseType.WEBHOOK ? { ...pauseMetadata, - id: engineResponseWatcher.getHandlerId() } : pauseMetadata - await flowRunRepo.update(flowRunId, { status: FlowRunStatus.PAUSED, logsFileId: logFileId, // eslint-disable-next-line @typescript-eslint/no-explicit-any - pauseMetadata: updatedPauseMetadata as any, + pauseMetadata: modifyPauseMetadata(pauseMetadata) as any, }) const flowRun = await flowRunRepo.findOneByOrFail({ id: flowRunId }) From 9c75b68b033184b2d1c99a84fe85c104fd18772f Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Thu, 9 May 2024 12:01:49 +0000 Subject: [PATCH 37/50] feat: add getFlowState to check state for running flow --- packages/shared/src/index.ts | 1 + packages/shared/src/lib/flow-run/flow-status.ts | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 packages/shared/src/lib/flow-run/flow-status.ts 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/flow-status.ts b/packages/shared/src/lib/flow-run/flow-status.ts new file mode 100644 index 0000000000..b9dafd3fc8 --- /dev/null +++ b/packages/shared/src/lib/flow-run/flow-status.ts @@ -0,0 +1,5 @@ +import { FlowRunStatus } from './execution/flow-execution' + +export const getFlowState = (status: FlowRunStatus): boolean => { + return status === FlowRunStatus.SUCCEEDED || status === FlowRunStatus.FAILED || status === FlowRunStatus.INTERNAL_ERROR || status === FlowRunStatus.QUOTA_EXCEEDED +} \ No newline at end of file From 087091e08773d3616ad394fd1bd175735c8adb9b Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Thu, 9 May 2024 12:02:42 +0000 Subject: [PATCH 38/50] fix: rename websocket event --- packages/server/api/src/app/flows/flow.module.ts | 8 ++++---- .../app/workers/flow-worker/engine-response-watcher.ts | 4 ++-- packages/shared/src/lib/websocket/index.ts | 2 +- .../test-flow-widget/test-flow-widget.component.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/server/api/src/app/flows/flow.module.ts b/packages/server/api/src/app/flows/flow.module.ts index 48d5ae8cb3..4dc87bbdc4 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, FlowRun, FlowRunStatus, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' +import { CreateStepRunRequestBody, FlowRun, getFlowState, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' export const flowModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(flowVersionController, { prefix: '/v1/flows' }) @@ -28,12 +28,12 @@ export const flowModule: FastifyPluginAsyncTypebox = async (app) => { socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_STARTED, flowRun) - eventEmitter.on('FlowStatus', (flowRunResponse: FlowRun) => { - if (flowRunResponse.status === FlowRunStatus.SUCCEEDED || flowRunResponse.status === FlowRunStatus.FAILED) { + eventEmitter.on(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS, (flowRunResponse: FlowRun) => { + if (getFlowState(flowRunResponse.status)) { eventEmitter.removeAllListeners() engineResponseWatcher.removeListener(flowRun.id) } - socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_FINISHED, flowRunResponse) + socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS, flowRunResponse) }) await engineResponseWatcher.listenAndEmit(flowRun.id, eventEmitter, flowRunService.getOneOrThrow({ id: flowRun.id, projectId: principal.projectId })) 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 285e489a0f..60386f0153 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 @@ -3,7 +3,7 @@ import { logger } from '@sentry/utils' import { StatusCodes } from 'http-status-codes' import { pubSub } from '../../helper/pubsub' import { system, SystemProp } from '@activepieces/server-shared' -import { apId, FlowRunStatus } from '@activepieces/shared' +import { apId, FlowRunStatus, WebsocketClientEvent } from '@activepieces/shared' const listeners = new Map void>() @@ -51,7 +51,7 @@ export const engineResponseWatcher = { const listenStatus = async () => { const finalFlowRun = await driver - event.emit('FlowStatus', finalFlowRun) + event.emit(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS, finalFlowRun) if (finalFlowRun.status !== FlowRunStatus.SUCCEEDED) { await listenStatus() } 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); From 22b931048794bdd2f7c87e097a326fc2373e2b14 Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Thu, 9 May 2024 12:23:59 +0000 Subject: [PATCH 39/50] fix: change the step run duration to be in seconds with up to 3 trailing decimals --- .../selected-step-result/selected-step-result.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/feature-builder-left-sidebar/src/lib/components/run-details/selected-step-result/selected-step-result.component.html b/packages/ui/feature-builder-left-sidebar/src/lib/components/run-details/selected-step-result/selected-step-result.component.html index 1b93646346..590b9f013a 100644 --- a/packages/ui/feature-builder-left-sidebar/src/lib/components/run-details/selected-step-result/selected-step-result.component.html +++ b/packages/ui/feature-builder-left-sidebar/src/lib/components/run-details/selected-step-result/selected-step-result.component.html @@ -20,7 +20,7 @@
Duration: - {{ (selectedStepResult.duration || 1) | number: '1.0-1' }} ms + {{ ((selectedStepResult.duration || 1) / 1000) | number: '1.1-3' }} s
Date: Thu, 9 May 2024 12:29:42 +0000 Subject: [PATCH 40/50] fix: flow state function naming --- packages/server/api/src/app/flows/flow.module.ts | 4 ++-- packages/shared/src/lib/flow-run/flow-status.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/api/src/app/flows/flow.module.ts b/packages/server/api/src/app/flows/flow.module.ts index 4dc87bbdc4..d4ed014300 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, FlowRun, getFlowState, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' +import { CreateStepRunRequestBody, FlowRun, isFlowStateTerminal, StepRunResponse, TestFlowRunRequestBody, WebsocketClientEvent, WebsocketServerEvent } from '@activepieces/shared' export const flowModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(flowVersionController, { prefix: '/v1/flows' }) @@ -29,7 +29,7 @@ export const flowModule: FastifyPluginAsyncTypebox = async (app) => { socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_STARTED, flowRun) eventEmitter.on(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS, (flowRunResponse: FlowRun) => { - if (getFlowState(flowRunResponse.status)) { + if (isFlowStateTerminal(flowRunResponse.status)) { eventEmitter.removeAllListeners() engineResponseWatcher.removeListener(flowRun.id) } diff --git a/packages/shared/src/lib/flow-run/flow-status.ts b/packages/shared/src/lib/flow-run/flow-status.ts index b9dafd3fc8..24411d3909 100644 --- a/packages/shared/src/lib/flow-run/flow-status.ts +++ b/packages/shared/src/lib/flow-run/flow-status.ts @@ -1,5 +1,5 @@ import { FlowRunStatus } from './execution/flow-execution' -export const getFlowState = (status: FlowRunStatus): boolean => { +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 From 2d3feb3344bb863c2c8d7912244dd489cb5d214c Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Thu, 9 May 2024 14:14:27 +0000 Subject: [PATCH 41/50] chore: lint codebase --- packages/pieces/community/pdf/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pieces/community/pdf/package.json b/packages/pieces/community/pdf/package.json index 6c38c0d693..0a7f23c062 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": "5.0.0", "tslib": "2.6.2" }, "main": "./src/index.js", From 5b10de482b95efb9e1c6a0dc2aaf1e7cae1d0fba Mon Sep 17 00:00:00 2001 From: Abdul-rahman Yasir Khalil Date: Thu, 9 May 2024 17:35:51 +0300 Subject: [PATCH 42/50] fix: flow table pieces icons were being duplicated --- .../pieces-icons-from-flow.component.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/ui/feature-pieces/src/lib/pieces-icons-from-flow/pieces-icons-from-flow.component.ts b/packages/ui/feature-pieces/src/lib/pieces-icons-from-flow/pieces-icons-from-flow.component.ts index 39ed920077..3363fd12b6 100644 --- a/packages/ui/feature-pieces/src/lib/pieces-icons-from-flow/pieces-icons-from-flow.component.ts +++ b/packages/ui/feature-pieces/src/lib/pieces-icons-from-flow/pieces-icons-from-flow.component.ts @@ -1,7 +1,11 @@ import { Component, Input, OnInit } from '@angular/core'; import { + Action, + ActionType, FlowVersion, FlowVersionTemplate, + Trigger, + TriggerType, flowHelper, } from '@activepieces/shared'; import { Observable, forkJoin, map } from 'rxjs'; @@ -34,7 +38,7 @@ export class PiecesIconsFromFlowComponent implements OnInit { constructor(private pieceService: PieceMetadataService) { } ngOnInit(): void { - const steps = flowHelper.getAllSteps(this.flowVersion.trigger); + const steps = this.filterOutDuplicates(flowHelper.getAllSteps(this.flowVersion.trigger)); const stepMetadata$: Observable[] = 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; + }); + } } From 6a6e183fdf1130d6c7c981e6ede7a86c0cade175 Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Thu, 9 May 2024 16:16:05 +0000 Subject: [PATCH 43/50] fix: infinte checking for run status --- package-lock.json | 56 ++++++++-------- package.json | 2 +- packages/pieces/community/pdf/package.json | 2 +- .../app-event-routing.module.ts | 3 +- .../app/flows/flow-run/flow-run-service.ts | 2 +- .../server/api/src/app/flows/flow.module.ts | 21 +++--- .../src/app/webhooks/webhook-controller.ts | 2 +- .../api/src/app/webhooks/webhook-service.ts | 1 - .../flow-worker/consumer/webook-consumer.ts | 13 +++- .../flow-worker/engine-response-watcher.ts | 64 +++++++++---------- packages/server/shared/package.json | 3 +- packages/server/shared/src/index.ts | 5 +- .../shared/src/lib/typed-event-emitter.ts | 17 +++++ 13 files changed, 108 insertions(+), 83 deletions(-) create mode 100644 packages/server/shared/src/lib/typed-event-emitter.ts diff --git a/package-lock.json b/package-lock.json index 1778664370..f352ea06cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", @@ -24227,9 +24222,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 +26588,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", @@ -30768,12 +30769,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 +34147,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 +34473,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 +34512,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": { diff --git a/package.json b/package.json index dda3b1bb62..ec2b5121d6 100644 --- a/package.json +++ b/package.json @@ -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/pdf/package.json b/packages/pieces/community/pdf/package.json index 0a7f23c062..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": "5.0.0", + "pdf-text-reader": "4.1.0", "tslib": "2.6.2" }, "main": "./src/index.js", 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/flows/flow-run/flow-run-service.ts b/packages/server/api/src/app/flows/flow-run/flow-run-service.ts index 392204a838..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 @@ -199,7 +199,7 @@ export const flowRunService = { projectId: flowRunToResume.projectId, flowVersionId: flowRunToResume.flowVersionId, synchronousHandlerId: returnHandlerId(pauseMetadata, requestId), - hookType: HookType.BEFORE_LOG, + hookType: HookType.AFTER_LOG, executionType, environment: RunEnvironment.PRODUCTION, }) diff --git a/packages/server/api/src/app/flows/flow.module.ts b/packages/server/api/src/app/flows/flow.module.ts index d4ed014300..d7bda432ec 100644 --- a/packages/server/api/src/app/flows/flow.module.ts +++ b/packages/server/api/src/app/flows/flow.module.ts @@ -1,4 +1,3 @@ -import EventEmitter from 'events' import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' import { accessTokenManager } from '../authentication/lib/access-token-manager' import { websocketService } from '../websockets/websockets.service' @@ -10,7 +9,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, FlowRun, isFlowStateTerminal, 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(flowVersionController, { prefix: '/v1/flows' }) @@ -19,7 +18,6 @@ export const flowModule: FastifyPluginAsyncTypebox = async (app) => { await app.register(testTriggerController, { prefix: '/v1/test-trigger' }) websocketService.addListener(WebsocketServerEvent.TEST_FLOW_RUN, (socket) => { return async (data: TestFlowRunRequestBody) => { - const eventEmitter = new EventEmitter() const principal = await accessTokenManager.extractPrincipal(socket.handshake.auth.token) const flowRun = await flowRunService.test({ projectId: principal.projectId, @@ -27,16 +25,19 @@ export const flowModule: FastifyPluginAsyncTypebox = async (app) => { }) socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_STARTED, flowRun) - - eventEmitter.on(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS, (flowRunResponse: FlowRun) => { - if (isFlowStateTerminal(flowRunResponse.status)) { - eventEmitter.removeAllListeners() + 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, flowRunResponse) + socket.emit(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS, flowRun) }) - - await engineResponseWatcher.listenAndEmit(flowRun.id, eventEmitter, flowRunService.getOneOrThrow({ id: flowRun.id, projectId: principal.projectId })) + } }) 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 c5f117765a..7abe7f1041 100644 --- a/packages/server/api/src/app/webhooks/webhook-controller.ts +++ b/packages/server/api/src/app/webhooks/webhook-controller.ts @@ -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 0c4b89cf3d..e9e52342c5 100644 --- a/packages/server/api/src/app/webhooks/webhook-service.ts +++ b/packages/server/api/src/app/webhooks/webhook-service.ts @@ -69,7 +69,6 @@ export const webhookService = { logger.info( `[WebhookService#callback] flowInstance not found or not enabled ignoring the webhook, flowId=${flow.id}`, ) - throw new ActivepiecesError({ code: ErrorCode.FLOW_NOT_FOUND, params: { 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..7f22c87f4a 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,15 @@ export const webhookConsumer = { }) return } + const isPublishedAndEnabled = flow.status !== FlowStatus.ENABLED || isNil(flow.publishedVersionId) + if (isPublishedAndEnabled) { + await stopAndReply(data, { + status: StatusCodes.NOT_FOUND, + body: {}, + headers: {}, + }) + return + } const handshakeResponse = await webhookService.handshake({ flow, payload, @@ -51,7 +60,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 60386f0153..36df16e7b8 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,11 +1,10 @@ -import { EventEmitter } from 'events' import { logger } from '@sentry/utils' import { StatusCodes } from 'http-status-codes' import { pubSub } from '../../helper/pubsub' -import { system, SystemProp } from '@activepieces/server-shared' -import { apId, FlowRunStatus, WebsocketClientEvent } from '@activepieces/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 @@ -13,7 +12,7 @@ export type EngineHttpResponse = { headers: Record } -type EngineResponseWithId = { +export type EngineResponseWithId = { requestId: string httpResponse: EngineHttpResponse } @@ -37,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}`, @@ -46,41 +44,39 @@ export const engineResponseWatcher = { }, ) }, - async listenAndEmit(requestId: string, event: EventEmitter, driver: Promise): Promise { - logger.info(`[engineWatcher#listenAndEmit] requestId=${requestId}`) - - const listenStatus = async () => { - const finalFlowRun = await driver - event.emit(WebsocketClientEvent.TEST_FLOW_RUN_PROGRESS, finalFlowRun) - if (finalFlowRun.status !== FlowRunStatus.SUCCEEDED) { - await listenStatus() - } - } - - await listenStatus() + listen(requestId: string): TypedEventEmitter { + const eventEmitter = new TypedEventEmitter() + logger.error('FUCK') + listeners.set(requestId, (data) => { + logger.error('ASH ' + data.requestId) + eventEmitter.emit(data) + }) + return eventEmitter }, - async listen(requestId: string, timeoutRequest: boolean): Promise { + 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) + } +} From 64d7546a1e2c8af75ea51c527a1a4c71c381e662 Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Thu, 9 May 2024 16:23:30 +0000 Subject: [PATCH 44/50] chore: remove debug logging --- .../api/src/app/workers/flow-worker/engine-response-watcher.ts | 2 -- 1 file changed, 2 deletions(-) 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 36df16e7b8..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 @@ -46,9 +46,7 @@ export const engineResponseWatcher = { }, listen(requestId: string): TypedEventEmitter { const eventEmitter = new TypedEventEmitter() - logger.error('FUCK') listeners.set(requestId, (data) => { - logger.error('ASH ' + data.requestId) eventEmitter.emit(data) }) return eventEmitter From 614a172e43dccbf0b0a111ee01b829997a9f0e3f Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Thu, 9 May 2024 17:24:06 +0000 Subject: [PATCH 45/50] feat: extract structured data --- packages/pieces/community/openai/package.json | 2 +- .../actions/extract-structure-data.action.ts | 27 +++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) 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." })); } }, From cda36ae9e086e09cc306d02a3dcb1555ada5178d Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Fri, 10 May 2024 00:32:59 +0000 Subject: [PATCH 46/50] fix: distinguish test webhooks to run when flow not published/enabled --- .../api/src/app/workers/flow-worker/consumer/webook-consumer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7f22c87f4a..2d7693c86a 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 @@ -17,7 +17,7 @@ export const webhookConsumer = { }) return } - const isPublishedAndEnabled = flow.status !== FlowStatus.ENABLED || isNil(flow.publishedVersionId) + const isPublishedAndEnabled = (flow.status !== FlowStatus.ENABLED || isNil(flow.publishedVersionId))&& !simulate if (isPublishedAndEnabled) { await stopAndReply(data, { status: StatusCodes.NOT_FOUND, From cb75a32f39e13c9b6524d9e10363a7d2b97ce583 Mon Sep 17 00:00:00 2001 From: Islam Abdelfattah Date: Fri, 10 May 2024 00:34:31 +0000 Subject: [PATCH 47/50] fix: add space --- .../api/src/app/workers/flow-worker/consumer/webook-consumer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2d7693c86a..5852cb8c2b 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 @@ -17,7 +17,7 @@ export const webhookConsumer = { }) return } - const isPublishedAndEnabled = (flow.status !== FlowStatus.ENABLED || isNil(flow.publishedVersionId))&& !simulate + const isPublishedAndEnabled = (flow.status !== FlowStatus.ENABLED || isNil(flow.publishedVersionId)) && !simulate if (isPublishedAndEnabled) { await stopAndReply(data, { status: StatusCodes.NOT_FOUND, From 78715c4c6ee4082a96313079199ecbf3df4c9777 Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Sun, 12 May 2024 20:46:23 +0000 Subject: [PATCH 48/50] fix(notion): new updated trigger --- packages/pieces/community/notion/package.json | 2 +- .../notion/src/lib/triggers/updated-database-item.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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, }; }); From bdabd757db50f707e21bfcbb0bb6de6f42b86278 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 May 2024 23:14:23 +0000 Subject: [PATCH 49/50] docs: update README.md [skip ci] --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 9ed6d1fbaf..d0d68dab45 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,9 @@ Not into coding but still interested in contributing? Come join our [Discord](ht Pratik Kinage
Pratik Kinage

🔌 Abdelrahman Mostafa
Abdelrahman Mostafa

🔌 + + Hamza Zagha
Hamza Zagha

🐛 + From e0559291a60eb15f368acd9b3ba25d0f2d637420 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 May 2024 23:14:24 +0000 Subject: [PATCH 50/50] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 59c42ce562..a150133190 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1028,6 +1028,15 @@ "contributions": [ "plugin" ] + }, + { + "login": "HamzaZagha", + "name": "Hamza Zagha", + "avatar_url": "https://avatars.githubusercontent.com/u/45468866?v=4", + "profile": "https://github.com/HamzaZagha", + "contributions": [ + "bug" + ] } ], "commitType": "docs"