From c6cf1174d8ce88d151d5e4178b4eb673f069bc45 Mon Sep 17 00:00:00 2001 From: Jorge Cortes Date: Wed, 9 Jul 2025 16:59:24 -0500 Subject: [PATCH 1/2] [ACTION] Clevertap new campaign components --- .../create-campaign/create-campaign.mjs | 318 ++++++++++++++++++ .../create-or-update-user.mjs | 2 +- .../get-campaign-report.mjs | 54 +++ .../actions/get-campaigns/get-campaigns.mjs | 43 +++ .../actions/stop-campaign/stop-campaign.mjs | 54 +++ .../actions/upload-events/upload-events.mjs | 2 +- components/clevertap/clevertap.app.mjs | 70 +++- components/clevertap/common/utils.mjs | 44 +++ components/clevertap/package.json | 4 +- pnpm-lock.yaml | 9 +- 10 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 components/clevertap/actions/create-campaign/create-campaign.mjs create mode 100644 components/clevertap/actions/get-campaign-report/get-campaign-report.mjs create mode 100644 components/clevertap/actions/get-campaigns/get-campaigns.mjs create mode 100644 components/clevertap/actions/stop-campaign/stop-campaign.mjs create mode 100644 components/clevertap/common/utils.mjs diff --git a/components/clevertap/actions/create-campaign/create-campaign.mjs b/components/clevertap/actions/create-campaign/create-campaign.mjs new file mode 100644 index 0000000000000..43a490b7eec0d --- /dev/null +++ b/components/clevertap/actions/create-campaign/create-campaign.mjs @@ -0,0 +1,318 @@ +import app from "../../clevertap.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "clevertap-create-campaign", + name: "Create Campaign", + description: "Create a campaign. [See the documentation](https://developer.clevertap.com/docs/create-campaign-api)", + version: "0.0.1", + type: "action", + props: { + app, + name: { + type: "string", + label: "Campaign Name", + description: "The name of your campaign shown in the CleverTap dashboard.", + }, + targetMode: { + type: "string", + label: "Target Mode", + description: "The type of campaign to create", + reloadProps: true, + options: [ + { + label: "Web Push Notification", + value: "webpush", + }, + { + label: "Mobile Push Notification", + value: "push", + }, + { + label: "Email", + value: "email", + }, + { + label: "Webhooks", + value: "webhook", + }, + ], + }, + where: { + type: "object", + label: "Where", + optional: true, + description: `Allows you to filter target base on the user events and profile properties. Omit this parameter to target your entire user base. **Example:** +\`\`\` +{ + "event_name": "Charged", + "from": 20250101, + "to": 20250402 +} +\`\`\``, + }, + content: { + type: "object", + label: "Content", + description: `Object that defines the content for your message. **Web Push Notification Example:** +\`\`\` +{ + "title": "Hi!", + "body": "How are you doing today?", + "platform_specific": { + "safari": { + "deep_link": "https://www.google.com", + "ttl": 10 + }, + "chrome": { + "image": "https://www.exampleImage.com", + "icon": "https://www.exampleIcon.com", + "deep_link": "http://www.example.com", + "ttl": 10, + "require_interaction": true, + "cta_title1": "title", + "cta_link1": "http://www.example2.com", + "cta_iconlink1": "https://www.exampleIcon2.com" + }, + "firefox": { + "icon": "https://www.exampleIcon.com", + "deep_link": "https://www.google.com", + "ttl": 10 + }, + "kaios": { + "icon": "https://www.exampleIcon.com", + "ttl": 10, + "kaiosKV": { + "key1": "value1", + "key2": "value2" + } + } + } +} +\`\`\`\n\n**Mobile Push Notification Example:** +\`\`\` +{ + "title": "Hi!", + "body": "How are you doing today?", + "platform_specific": { + "ios": { + "mutable-content": "true", + "deep_link": "example.com", + "sound_file": "example.caf", + "category": "application category//Books", + "badge_count": 1, + "Key": "Value_ios" + }, + "android": { + "enable_rendermax": true, + "wzrk_cid": "Marketing", + "background_image": "http://example.jpg", + "default_sound": true, + "deep_link": "example.com", + "large_icon": "http://example.png", + "Key": "Value_android", + } + } +} +\`\`\`\n\n **Email Example:** +\`\`\` +{ + "subject": "Welcome", + "body": "
Your HTML content for the email
", + "sender_name": "CleverTap" +} +\`\`\``, + optional: true, + }, + respectFrequencyCaps: { + type: "boolean", + label: "Respect Frequency Caps", + description: "Set to `false` if you want to override frequency caps and dwell time. Defaults to `true`.", + optional: true, + }, + estimateOnly: { + type: "boolean", + label: "Estimate Only", + description: "If this parameter is set to `true`, the request returns an estimated reach of the campaign, which is the number of users who receive the notification when you send it out. Setting this parameter to `true` does not create the campaign. Defaults to `false`.", + optional: true, + }, + conversionGoal: { + type: "object", + label: "Conversion Goal", + optional: true, + description: `Checks the end goal of the campaign for conversion tracking. **Example:** +\`\`\` +{ + "event_name": "Charged", + "filter_type": { + "event_property": [ + { + "property_name": "Items|Book name", + "operator": "equals", + "property_value": "book name" + }, + { + "property_name": "Amount", + "operator": "equals", + "property_value": 3456 + }, + { + "property_name": "Currency", + "operator": "equals", + "property_value": "USD" + } + ], + "first_time": "yes", + "last_time": "yes", + "time_of_day": [["21:00","23:00"],["11:34","12:44"],["13:01","13:40"]], + "day_of_week": [1,7], + "day_of_month": [1,3,16,31] + }, + "conversion_time": "1D", + "revenue_property": "Amount" +} +\`\`\``, + }, + ttlType: { + type: "string", + label: "Time To Live Type", + description: "This allows setting of time to live for push notifications for android and iOS. The type of time to live. Can be `absolute` or `relative`. Defaults to `absolute`.", + options: [ + { + label: "Absolute", + value: "absolute", + }, + { + label: "Relative", + value: "relative", + }, + ], + optional: true, + }, + ttlValue: { + type: "integer", + label: "Time To Live Value", + description: "This allows setting of time to live for push notifications for android and iOS. The value of the time to live. Can be a number of days, hours, minutes, or seconds.\n- For `relative` ttl, the input is an integer in minutes\n- For `absolute` ttl, the input should be an integer in the `yyyyMMddHHmm` format. Example: `202207200000`", + optional: true, + }, + webhookEndpointName: { + type: "string", + label: "Webhook Endpoint Name", + description: "The name of the webhook endpoint to use for the campaign.", + optional: true, + }, + webhookFields: { + type: "string[]", + label: "Webhook Fields", + description: "The fields to include in the webhook payload. Can be `profile-attributes`, `tokens`, or `identities`.", + optional: true, + }, + webhookKeyValue: { + type: "object", + label: "Webhook Key Value", + description: "The key-value pairs to include in the webhook payload.", + optional: true, + }, + }, + additionalProps() { + if (this.targetMode === "push") { + return { + when: { + type: "object", + label: "When", + description: `Allows you to set Date, time and Delivery preferences of the message. **Example:** +\`\`\` +{ + "type": "later", + "delivery_date_time": ["20230503 15:20"], + "delivery_timezone": "user", + "user_timezone_wrap_around": true, + "dnd": { + "message_state": "delay", + "dnd_timezone": "user" + }, + "campaign_cutoff": "14:20" +} +\`\`\``, + }, + }; + } + + return { + when: { + type: "string", + label: "When", + description: "When you want to send out the messages. Valid inputs are `now` (to send the notification right away) or in the format **YYYYMMDD HH:MM** to schedule the messages for a specific date and time in the future. **Example:** `20250717 15:20`", + }, + }; + }, + async run({ $ }) { + const { + app, + name, + targetMode, + when, + where, + content, + respectFrequencyCaps, + estimateOnly, + conversionGoal, + ttlType, + ttlValue, + webhookEndpointName, + webhookFields, + webhookKeyValue, + } = this; + + const response = await app.createCampaign({ + $, + debug: true, + data: { + name, + target_mode: targetMode, + respect_frequency_caps: respectFrequencyCaps, + estimate_only: estimateOnly, + when: utils.parseJson(when) || {}, + where: utils.parseJson(where) || {}, + content: utils.parseJson(content) || {}, + ...(conversionGoal + ? { + conversion_goal: utils.parseJson(conversionGoal), + } + : {} + ), + ...(ttlType && ttlValue + ? { + ttl: { + ttl_type: ttlType, + value: ttlValue, + }, + } + : {} + ), + ...(webhookEndpointName + ? { + webhook_endpoint_name: webhookEndpointName, + } + : {} + ), + ...(webhookFields + ? { + webhook_fields: utils.parseJson(webhookFields), + } + : {} + ), + ...(webhookKeyValue + ? { + webhook_key_value: utils.parseJson(webhookKeyValue), + } + : {} + ), + }, + }); + + $.export("$summary", "Successfully created campaign"); + + return response; + }, +}; diff --git a/components/clevertap/actions/create-or-update-user/create-or-update-user.mjs b/components/clevertap/actions/create-or-update-user/create-or-update-user.mjs index fa59c5021de1d..e003b733bbf44 100644 --- a/components/clevertap/actions/create-or-update-user/create-or-update-user.mjs +++ b/components/clevertap/actions/create-or-update-user/create-or-update-user.mjs @@ -2,7 +2,7 @@ import app from "../../clevertap.app.mjs"; export default { name: "Create Or Update User", - version: "0.0.1", + version: "0.0.2", key: "clevertap-create-or-update-user", description: "Create or update an user. [See the documentation](https://developer.clevertap.com/docs/upload-user-profiles-api)", type: "action", diff --git a/components/clevertap/actions/get-campaign-report/get-campaign-report.mjs b/components/clevertap/actions/get-campaign-report/get-campaign-report.mjs new file mode 100644 index 0000000000000..59181ecfcd78f --- /dev/null +++ b/components/clevertap/actions/get-campaign-report/get-campaign-report.mjs @@ -0,0 +1,54 @@ +import app from "../../clevertap.app.mjs"; + +export default { + key: "clevertap-get-campaign-report", + name: "Get Campaign Report", + description: "Get the report for a completed campaign. [See the documentation](https://developer.clevertap.com/docs/get-campaign-report-api)", + version: "0.0.1", + type: "action", + props: { + app, + from: { + propDefinition: [ + app, + "from", + ], + }, + to: { + propDefinition: [ + app, + "to", + ], + }, + campaignId: { + description: "The ID of the campaign to retrieve the report for.", + propDefinition: [ + app, + "campaignId", + ({ + from, to, + }) => ({ + from, + to, + }), + ], + }, + }, + async run({ $ }) { + const { + app, + campaignId, + } = this; + + const response = await app.getCampaignReport({ + $, + data: { + id: campaignId, + }, + }); + + $.export("$summary", "Successfully retrieved report for campaign"); + + return response; + }, +}; diff --git a/components/clevertap/actions/get-campaigns/get-campaigns.mjs b/components/clevertap/actions/get-campaigns/get-campaigns.mjs new file mode 100644 index 0000000000000..ec7d9130bc111 --- /dev/null +++ b/components/clevertap/actions/get-campaigns/get-campaigns.mjs @@ -0,0 +1,43 @@ +import app from "../../clevertap.app.mjs"; + +export default { + key: "clevertap-get-campaigns", + name: "Get Campaigns", + description: "Get a list of campaigns within a specified date range. [See the documentation](https://developer.clevertap.com/docs/get-campaigns-api)", + version: "0.0.1", + type: "action", + props: { + app, + from: { + propDefinition: [ + app, + "from", + ], + }, + to: { + propDefinition: [ + app, + "to", + ], + }, + }, + async run({ $ }) { + const { + app, + from, + to, + } = this; + + const response = await app.getCampaigns({ + $, + data: { + from, + to, + }, + }); + + $.export("$summary", "Successfully retrieved campaigns"); + + return response; + }, +}; diff --git a/components/clevertap/actions/stop-campaign/stop-campaign.mjs b/components/clevertap/actions/stop-campaign/stop-campaign.mjs new file mode 100644 index 0000000000000..de7388af8bdda --- /dev/null +++ b/components/clevertap/actions/stop-campaign/stop-campaign.mjs @@ -0,0 +1,54 @@ +import app from "../../clevertap.app.mjs"; + +export default { + key: "clevertap-stop-campaign", + name: "Stop Campaign", + description: "Stop a running campaign. [See the documentation](https://developer.clevertap.com/docs/stop-campaign-api)", + version: "0.0.1", + type: "action", + props: { + app, + from: { + propDefinition: [ + app, + "from", + ], + }, + to: { + propDefinition: [ + app, + "to", + ], + }, + campaignId: { + propDefinition: [ + app, + "campaignId", + ({ + from, + to, + }) => ({ + from, + to, + }), + ], + }, + }, + async run({ $ }) { + const { + app, + campaignId, + } = this; + + const response = await app.stopCampaign({ + $, + data: { + id: campaignId, + }, + }); + + $.export("$summary", "Successfully stopped campaign"); + + return response; + }, +}; diff --git a/components/clevertap/actions/upload-events/upload-events.mjs b/components/clevertap/actions/upload-events/upload-events.mjs index 1857f3c825c6b..9734da18475c1 100644 --- a/components/clevertap/actions/upload-events/upload-events.mjs +++ b/components/clevertap/actions/upload-events/upload-events.mjs @@ -2,7 +2,7 @@ import app from "../../clevertap.app.mjs"; export default { name: "Upload Events", - version: "0.0.1", + version: "0.0.2", key: "clevertap-upload-events", description: "Upload events. [See the documentation](https://developer.clevertap.com/docs/upload-events-api)", type: "action", diff --git a/components/clevertap/clevertap.app.mjs b/components/clevertap/clevertap.app.mjs index 608198ca82db4..8a23a42c126ca 100644 --- a/components/clevertap/clevertap.app.mjs +++ b/components/clevertap/clevertap.app.mjs @@ -3,7 +3,42 @@ import { axios } from "@pipedream/platform"; export default { type: "app", app: "clevertap", - propDefinitions: {}, + propDefinitions: { + from: { + type: "integer", + label: "From Date", + description: "Start of the date range for which you want to retrieve campaigns. Use `YYYYMMDD` format.", + }, + to: { + type: "integer", + label: "To Date", + description: "End of the date range for which you want to retrieve campaigns. Use `YYYYMMDD` format.", + }, + campaignId: { + type: "integer", + label: "Campaign ID", + description: "The ID of the campaign you want to stop.", + async options({ + from, to, + }) { + if (!from || !to) { + return []; + } + const { targets } = await this.getCampaigns({ + data: { + from, + to, + }, + }); + return targets?.map(({ + id, name, + }) => ({ + label: name, + value: id, + })) || []; + }, + }, + }, methods: { _projectId() { return this.$auth.project_id; @@ -17,7 +52,7 @@ export default { _apiUrl() { return `https://${this._region()}.clevertap.com/1`; }, - async _makeRequest({ + _makeRequest({ $ = this, path, ...args }) { return axios($, { @@ -27,10 +62,39 @@ export default { ...args.headers, "X-CleverTap-Account-Id": this._projectId(), "X-CleverTap-Passcode": this._passCode(), + "Content-Type": "application/json", }, }); }, - async uploadEvent(args = {}) { + getCampaigns(args = {}) { + return this._makeRequest({ + path: "/targets/list.json", + method: "post", + ...args, + }); + }, + createCampaign(args = {}) { + return this._makeRequest({ + path: "/targets/create.json", + method: "post", + ...args, + }); + }, + getCampaignReport(args = {}) { + return this._makeRequest({ + path: "/targets/result.json", + method: "post", + ...args, + }); + }, + stopCampaign(args = {}) { + return this._makeRequest({ + path: "/targets/stop.json", + method: "post", + ...args, + }); + }, + uploadEvent(args = {}) { return this._makeRequest({ path: "/upload", method: "post", diff --git a/components/clevertap/common/utils.mjs b/components/clevertap/common/utils.mjs new file mode 100644 index 0000000000000..2adf04343104f --- /dev/null +++ b/components/clevertap/common/utils.mjs @@ -0,0 +1,44 @@ +const parseJson = (input, maxDepth = 100) => { + const seen = new WeakSet(); + const parse = (value) => { + if (maxDepth <= 0) { + return value; + } + if (typeof(value) === "string") { + // Only parse if the string looks like a JSON object or array + const trimmed = value.trim(); + if ( + (trimmed.startsWith("{") && trimmed.endsWith("}")) || + (trimmed.startsWith("[") && trimmed.endsWith("]")) + ) { + try { + return parseJson(JSON.parse(value), maxDepth - 1); + } catch (e) { + return value; + } + } + return value; + } else if (typeof(value) === "object" && value !== null && !Array.isArray(value)) { + if (seen.has(value)) { + return value; + } + seen.add(value); + return Object.entries(value) + .reduce((acc, [ + key, + val, + ]) => Object.assign(acc, { + [key]: parse(val), + }), {}); + } else if (Array.isArray(value)) { + return value.map((item) => parse(item)); + } + return value; + }; + + return parse(input); +}; + +export default { + parseJson, +}; diff --git a/components/clevertap/package.json b/components/clevertap/package.json index a4c4ed44a5aa7..93c7ea4a041bd 100644 --- a/components/clevertap/package.json +++ b/components/clevertap/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/clevertap", - "version": "0.1.0", + "version": "0.2.0", "description": "Pipedream CleverTap Components", "main": "clevertap.app.mjs", "keywords": [ @@ -13,6 +13,6 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^1.5.1" + "@pipedream/platform": "^3.1.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ed82818cede7..3ef94120d8f6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -719,8 +719,7 @@ importers: specifier: ^3.1.0 version: 3.1.0 - components/alt_text_lab: - specifiers: {} + components/alt_text_lab: {} components/alteryx_analytics_cloud: {} @@ -2566,8 +2565,8 @@ importers: components/clevertap: dependencies: '@pipedream/platform': - specifier: ^1.5.1 - version: 1.6.6 + specifier: ^3.1.0 + version: 3.1.0 components/click2mail2: dependencies: @@ -37252,8 +37251,6 @@ snapshots: '@putout/operator-filesystem': 5.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3)) '@putout/operator-json': 2.2.0 putout: 36.13.1(eslint@8.57.1)(typescript@5.6.3) - transitivePeerDependencies: - - supports-color '@putout/operator-regexp@1.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3))': dependencies: From 6962ae85ecbd93139ae6434a60bb5b5a60363e8c Mon Sep 17 00:00:00 2001 From: Leo Vu Date: Fri, 18 Jul 2025 12:08:32 +0700 Subject: [PATCH 2/2] Update components/clevertap/actions/create-campaign/create-campaign.mjs --- components/clevertap/actions/create-campaign/create-campaign.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/components/clevertap/actions/create-campaign/create-campaign.mjs b/components/clevertap/actions/create-campaign/create-campaign.mjs index 43a490b7eec0d..793dbfeb917d2 100644 --- a/components/clevertap/actions/create-campaign/create-campaign.mjs +++ b/components/clevertap/actions/create-campaign/create-campaign.mjs @@ -266,7 +266,6 @@ export default { const response = await app.createCampaign({ $, - debug: true, data: { name, target_mode: targetMode,