From b79de9b1ee1bdc5b908194c603fb933d0d48afaa Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 6 Jan 2025 17:06:14 -0300 Subject: [PATCH 1/7] easypromos init --- components/easypromos/easypromos.app.mjs | 117 ++++++++++++++- components/easypromos/package.json | 2 +- .../new-coin-transaction.mjs | 141 ++++++++++++++++++ .../new-participation/new-participation.mjs | 95 ++++++++++++ .../easypromos/sources/new-user/new-user.mjs | 82 ++++++++++ 5 files changed, 433 insertions(+), 4 deletions(-) create mode 100644 components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs create mode 100644 components/easypromos/sources/new-participation/new-participation.mjs create mode 100644 components/easypromos/sources/new-user/new-user.mjs diff --git a/components/easypromos/easypromos.app.mjs b/components/easypromos/easypromos.app.mjs index 2b6dbbddc801a..f5aa6b33df964 100644 --- a/components/easypromos/easypromos.app.mjs +++ b/components/easypromos/easypromos.app.mjs @@ -1,11 +1,122 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "easypromos", - propDefinitions: {}, + version: "0.0.{{ts}}", + propDefinitions: { + userid: { + type: "integer", + label: "User ID", + description: "The ID of the user", + async options() { + const users = await this.getUsers(); + return users.map((user) => ({ + label: user.name, + value: user.id, + })); + }, + }, + promotionid: { + type: "integer", + label: "Promotion ID", + description: "The ID of the promotion", + async options() { + const promotions = await this.getPromotions(); + return promotions.map((promotion) => ({ + label: promotion.name, + value: promotion.id, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data + // Existing method authKeys() { console.log(Object.keys(this.$auth)); }, + // Base URL for the Easypromos API + _baseUrl() { + return "https://api.easypromos.com"; + }, + // Helper method to make HTTP requests + async _makeRequest(opts = {}) { + const { + $ = this, + method = "GET", + path = "/", + data, + params, + headers = {}, + ...otherOpts + } = opts; + + const requestHeaders = { + ...headers, + Authorization: `Bearer ${this.$auth.access_token}`, + }; + + if (data) { + requestHeaders["Content-Type"] = "application/json"; + } + + return axios($, { + method, + url: this._baseUrl() + path, + headers: requestHeaders, + data, + params, + ...otherOpts, + }); + }, + // Method to fetch users + async getUsers(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/users", + params: opts.params, + }); + }, + // Method to fetch promotions + async getPromotions(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/promotions", + params: opts.params, + }); + }, + // Emit event when a user earns or spends coins + async emitCoinTransaction({ + userid, promotionid, + }) { + return this._makeRequest({ + method: "POST", + path: "/coin-transactions", + data: { + user_id: userid, + promotion_id: promotionid, + }, + }); + }, + // Emit event when a registered user submits participation + async emitParticipationSubmission({ promotionid }) { + return this._makeRequest({ + method: "POST", + path: "/participations", + data: { + promotion_id: promotionid, + }, + }); + }, + // Emit event when a user registers in the promotion + async emitUserRegistration({ promotionid }) { + return this._makeRequest({ + method: "POST", + path: "/registrations", + data: { + promotion_id: promotionid, + }, + }); + }, }, -}; \ No newline at end of file +}; diff --git a/components/easypromos/package.json b/components/easypromos/package.json index 34a1b87bc7549..3f4ec22db5dd6 100644 --- a/components/easypromos/package.json +++ b/components/easypromos/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs b/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs new file mode 100644 index 0000000000000..806e8e11a3749 --- /dev/null +++ b/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs @@ -0,0 +1,141 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import easypromos from "../../easypromos.app.mjs"; + +export default { + key: "easypromos-new-coin-transaction", + name: "New Coin Transaction", + description: "Emit new event when a user earns or spends coins. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + easypromos, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + userid: { + propDefinition: [ + easypromos, + "userid", + ], + }, + promotionid: { + propDefinition: [ + easypromos, + "promotionid", + ], + }, + }, + methods: { + async getCoinTransactions(page = 1, perPage = 50, since = null) { + const params = { + promotion_id: this.promotionid, + user_id: this.userid, + page, + perpage: perPage, + }; + if (since) { + params.since = since; + } + const response = await this.easypromos._makeRequest({ + method: "GET", + path: "/coin-transactions", + params, + }); + return response.transactions || []; + }, + async _getLastTimestamp() { + return (await this.db.get("lastTimestamp")) || 0; + }, + async _setLastTimestamp(timestamp) { + await this.db.set("lastTimestamp", timestamp); + }, + }, + hooks: { + async deploy() { + try { + let page = 1; + const perPage = 50; + const fetchedTransactions = []; + const lastTimestamp = await this._getLastTimestamp(); + + while (fetchedTransactions.length < perPage) { + const transactions = await this.getCoinTransactions(page, perPage, lastTimestamp); + if (transactions.length === 0) break; + fetchedTransactions.unshift(...transactions.reverse()); + if (transactions.length < perPage) break; + page += 1; + } + + const recentTransactions = fetchedTransactions.slice(0, perPage); + for (const transaction of recentTransactions) { + this.$emit(transaction, { + id: transaction.id.toString(), + summary: `Coin transaction: ${transaction.amount} for user ${transaction.user_id}`, + ts: Date.parse(transaction.created), + }); + } + + if (recentTransactions.length > 0) { + const latestTimestamp = Date.parse(recentTransactions[0].created); + await this._setLastTimestamp(latestTimestamp); + } + } catch (error) { + this.logger.error(`Error deploying source: ${error.message}`); + } + }, + async activate() { + // No webhook setup required + }, + async deactivate() { + // No webhook teardown required + }, + }, + async run() { + try { + const lastTimestamp = await this._getLastTimestamp(); + let page = 1; + const perPage = 50; + const newTransactions = []; + + while (true) { + const transactions = await this.getCoinTransactions(page, perPage, lastTimestamp); + if (transactions.length === 0) break; + + for (const transaction of transactions) { + const transactionTimestamp = Date.parse(transaction.created); + if (transactionTimestamp > lastTimestamp) { + newTransactions.push(transaction); + } + } + + if (transactions.length < perPage) break; + page += 1; + } + + for (const transaction of newTransactions) { + this.$emit(transaction, { + id: transaction.id.toString(), + summary: `Coin transaction: ${transaction.amount} for user ${transaction.user_id}`, + ts: Date.parse(transaction.created), + }); + } + + if (newTransactions.length > 0) { + const latestTransaction = newTransactions.reduce((a, b) => + Date.parse(a.created) > Date.parse(b.created) + ? a + : b); + await this._setLastTimestamp(Date.parse(latestTransaction.created)); + } + } catch (error) { + this.logger.error(`Error running source: ${error.message}`); + } + }, +}; diff --git a/components/easypromos/sources/new-participation/new-participation.mjs b/components/easypromos/sources/new-participation/new-participation.mjs new file mode 100644 index 0000000000000..c0489f4b40767 --- /dev/null +++ b/components/easypromos/sources/new-participation/new-participation.mjs @@ -0,0 +1,95 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import appName from "../../easypromos.app.mjs"; + +export default { + key: "easypromos-new-participation", + name: "New Participation Submitted", + description: "Emit new event when a registered user submits a participation in the promotion. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + easypromos: { + type: "app", + app: "easypromos", + }, + promotionid: { + propDefinition: [ + appName, + "promotionid", + ], + }, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + hooks: { + async deploy() { + const lastParticipationId = this.db.get("lastParticipationId") || 0; + const participations = await this.easypromos._makeRequest({ + method: "GET", + path: "/participations", + params: { + promotion_id: this.promotionid, + limit: 50, + sort: "id_desc", + }, + }); + + const newParticipations = participations.filter((participation) => participation.id > lastParticipationId); + + newParticipations.forEach((participation) => { + this.$emit(participation, { + id: participation.id.toString(), + summary: `New Participation by User ID ${participation.user_id}`, + ts: Date.parse(participation.created), + }); + }); + + if (participations.length > 0) { + const maxId = Math.max(...participations.map((p) => p.id)); + this.db.set("lastParticipationId", maxId); + } + }, + async activate() { + // No webhook setup needed for polling + }, + async deactivate() { + // No webhook teardown needed for polling + }, + }, + async run() { + const lastParticipationId = this.db.get("lastParticipationId") || 0; + + const participations = await this.easypromos._makeRequest({ + method: "GET", + path: "/participations", + params: { + promotion_id: this.promotionid, + limit: 50, + sort: "id_desc", + }, + }); + + const newParticipations = participations.filter((participation) => participation.id > lastParticipationId); + + newParticipations.forEach((participation) => { + this.$emit(participation, { + id: participation.id.toString(), + summary: `New Participation by User ID ${participation.user_id}`, + ts: Date.parse(participation.created) || Date.now(), + }); + }); + + if (participations.length > 0) { + const maxId = Math.max(...participations.map((p) => p.id)); + this.db.set("lastParticipationId", maxId); + } + }, +}; diff --git a/components/easypromos/sources/new-user/new-user.mjs b/components/easypromos/sources/new-user/new-user.mjs new file mode 100644 index 0000000000000..82cdfe260f17a --- /dev/null +++ b/components/easypromos/sources/new-user/new-user.mjs @@ -0,0 +1,82 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import easypromos from "../../easypromos.app.mjs"; + +export default { + key: "easypromos-new-user", + name: "New User Registration", + description: "Emits a new event when a user registers in the promotion. [See the documentation]().", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + easypromos, + promotionid: { + propDefinition: [ + easypromos, + "promotionid", + ], + }, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + hooks: { + async deploy() { + const lastTs = 0; + await this.db.set("lastTs", lastTs); + const registrations = await this.easypromos.getUsers({ + params: { + promotion_id: this.promotionid, + since: lastTs, + per_page: 50, + }, + }); + for (const registration of registrations.slice(0, 50)) { + const ts = Date.parse(registration.created_at) || Date.now(); + this.$emit(registration, { + id: registration.id || ts, + summary: `New User Registration: ${registration.name}`, + ts, + }); + } + if (registrations.length > 0) { + const latestTs = Math.max(...registrations.map((r) => Date.parse(r.created_at) || 0)); + await this.db.set("lastTs", latestTs); + } + }, + async activate() { + // Placeholder for activation logic if needed + }, + async deactivate() { + // Placeholder for deactivation logic if needed + }, + }, + async run() { + const lastTs = await this.db.get("lastTs") || 0; + const registrations = await this.easypromos.getUsers({ + params: { + promotion_id: this.promotionid, + since: lastTs, + per_page: 50, + }, + }); + for (const registration of registrations) { + const ts = Date.parse(registration.created_at) || Date.now(); + this.$emit(registration, { + id: registration.id || ts, + summary: `New User Registration: ${registration.name}`, + ts, + }); + } + if (registrations.length > 0) { + const latestTs = Math.max(...registrations.map((r) => Date.parse(r.created_at) || 0)); + await this.db.set("lastTs", latestTs); + } + }, +}; From 45e5a208e7f31d7df55620f49f341b48fa908489 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 7 Jan 2025 12:53:12 -0300 Subject: [PATCH 2/7] [Components] easypromos #15197 Sources - New Coin Transaction - New Participation - New User --- components/easypromos/easypromos.app.mjs | 179 ++++++++++-------- components/easypromos/package.json | 5 +- components/easypromos/sources/common/base.mjs | 66 +++++++ .../new-coin-transaction.mjs | 145 ++------------ .../new-coin-transaction/test-event.mjs | 54 ++++++ .../new-participation/new-participation.mjs | 99 ++-------- .../sources/new-participation/test-event.mjs | 19 ++ .../easypromos/sources/new-user/new-user.mjs | 86 ++------- .../sources/new-user/test-event.mjs | 39 ++++ 9 files changed, 341 insertions(+), 351 deletions(-) create mode 100644 components/easypromos/sources/common/base.mjs create mode 100644 components/easypromos/sources/new-coin-transaction/test-event.mjs create mode 100644 components/easypromos/sources/new-participation/test-event.mjs create mode 100644 components/easypromos/sources/new-user/test-event.mjs diff --git a/components/easypromos/easypromos.app.mjs b/components/easypromos/easypromos.app.mjs index f5aa6b33df964..d2cb20c897d49 100644 --- a/components/easypromos/easypromos.app.mjs +++ b/components/easypromos/easypromos.app.mjs @@ -3,120 +3,137 @@ import { axios } from "@pipedream/platform"; export default { type: "app", app: "easypromos", - version: "0.0.{{ts}}", propDefinitions: { - userid: { + userId: { type: "integer", label: "User ID", description: "The ID of the user", - async options() { - const users = await this.getUsers(); - return users.map((user) => ({ - label: user.name, - value: user.id, - })); + async options({ + promotionId, prevContext, + }) { + const { + items, paging, + } = await this.getUsers({ + promotionId, + params: { + next_cursor: prevContext.nextCursor, + }, + }); + return { + options: items.map(({ + id: value, email: label, + }) => ({ + label, + value, + })), + context: { + nextCursor: paging.next_cursor, + }, + }; }, }, - promotionid: { + promotionId: { type: "integer", label: "Promotion ID", description: "The ID of the promotion", - async options() { - const promotions = await this.getPromotions(); - return promotions.map((promotion) => ({ - label: promotion.name, - value: promotion.id, - })); + async options({ prevContext }) { + const { + items, paging, + } = await this.getPromotions({ + params: { + next_cursor: prevContext.nextCursor, + }, + }); + return { + options: items.map(({ + id: value, title, internal_ref: ref, + }) => ({ + label: ref || title, + value, + })), + context: { + nextCursor: paging.next_cursor, + }, + }; }, }, }, methods: { - // Existing method - authKeys() { - console.log(Object.keys(this.$auth)); - }, - // Base URL for the Easypromos API _baseUrl() { - return "https://api.easypromos.com"; + return "https://api.easypromosapp.com/v2"; }, - // Helper method to make HTTP requests - async _makeRequest(opts = {}) { - const { - $ = this, - method = "GET", - path = "/", - data, - params, - headers = {}, - ...otherOpts - } = opts; - - const requestHeaders = { - ...headers, - Authorization: `Bearer ${this.$auth.access_token}`, + _headers() { + return { + Authorization: `Bearer ${this.$auth.api_key}`, }; - - if (data) { - requestHeaders["Content-Type"] = "application/json"; - } - + }, + _makeRequest({ + $ = this, path, ...opts + }) { return axios($, { - method, url: this._baseUrl() + path, - headers: requestHeaders, - data, - params, - ...otherOpts, + headers: this._headers(), + ...opts, }); }, - // Method to fetch users - async getUsers(opts = {}) { + getCoinTransactions({ + promotionId, ...opts + }) { return this._makeRequest({ - method: "GET", - path: "/users", - params: opts.params, + path: `/coin_transactions/${promotionId}`, + ...opts, }); }, - // Method to fetch promotions - async getPromotions(opts = {}) { + getUsers({ + promotionId, ...opts + }) { return this._makeRequest({ - method: "GET", - path: "/promotions", - params: opts.params, + path: `/users/${promotionId}`, + ...opts, }); }, - // Emit event when a user earns or spends coins - async emitCoinTransaction({ - userid, promotionid, + getParticipations({ + promotionId, ...opts }) { return this._makeRequest({ - method: "POST", - path: "/coin-transactions", - data: { - user_id: userid, - promotion_id: promotionid, - }, + path: `/participations/${promotionId}`, + ...opts, }); }, - // Emit event when a registered user submits participation - async emitParticipationSubmission({ promotionid }) { + getPromotions(opts = {}) { return this._makeRequest({ - method: "POST", - path: "/participations", - data: { - promotion_id: promotionid, - }, + path: "/promotions", + ...opts, }); }, - // Emit event when a user registers in the promotion - async emitUserRegistration({ promotionid }) { - return this._makeRequest({ - method: "POST", - path: "/registrations", - data: { - promotion_id: promotionid, - }, - }); + async *paginate({ + fn, params = {}, maxResults = null, ...opts + }) { + let hasMore = false; + let count = 0; + let nextCursor = null; + + do { + params.next_cursor = nextCursor; + const { + items, + paging: { next_cursor }, + } = await fn({ + params, + ...opts, + }); + for (const d of items) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + nextCursor = next_cursor; + hasMore = nextCursor; + + } while (hasMore); }, }, }; diff --git a/components/easypromos/package.json b/components/easypromos/package.json index 3f4ec22db5dd6..ff4fa8d3e4e16 100644 --- a/components/easypromos/package.json +++ b/components/easypromos/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/easypromos", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Easypromos Components", "main": "easypromos.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/easypromos/sources/common/base.mjs b/components/easypromos/sources/common/base.mjs new file mode 100644 index 0000000000000..09741c6c97a42 --- /dev/null +++ b/components/easypromos/sources/common/base.mjs @@ -0,0 +1,66 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import easypromos from "../../easypromos.app.mjs"; + +export default { + props: { + easypromos, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastId() { + return this.db.get("lastId") || 0; + }, + _setLastId(lastId) { + this.db.set("lastId", lastId); + }, + getOpts() { + return {}; + }, + async emitEvent(maxResults = false) { + const lastId = this._getLastId(); + + const response = this.easypromos.paginate({ + fn: this.getFunction(), + ...this.getOpts(), + params: { + order: "created_desc", + }, + }); + + let responseArray = []; + for await (const item of response) { + if (item.id <= lastId) break; + responseArray.push(item); + } + + if (responseArray.length) { + if (maxResults && (responseArray.length > maxResults)) { + responseArray.length = maxResults; + } + this._setLastId(responseArray[0].id); + } + + for (const item of responseArray.reverse()) { + this.$emit(item, { + id: item.id || item.transaction.id, + summary: this.getSummary(item), + ts: Date.parse(item.created || new Date()), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs b/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs index 806e8e11a3749..16770095679fb 100644 --- a/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs +++ b/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs @@ -1,141 +1,38 @@ -import { - axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, -} from "@pipedream/platform"; -import easypromos from "../../easypromos.app.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "easypromos-new-coin-transaction", name: "New Coin Transaction", - description: "Emit new event when a user earns or spends coins. [See the documentation]()", - version: "0.0.{{ts}}", + description: "Emit new event when a user earns or spends coins.", + version: "0.0.1", type: "source", dedupe: "unique", props: { - easypromos, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, - }, - }, - userid: { + ...common.props, + promotionId: { propDefinition: [ - easypromos, - "userid", - ], - }, - promotionid: { - propDefinition: [ - easypromos, - "promotionid", + common.props.easypromos, + "promotionId", ], }, }, methods: { - async getCoinTransactions(page = 1, perPage = 50, since = null) { - const params = { - promotion_id: this.promotionid, - user_id: this.userid, - page, - perpage: perPage, - }; - if (since) { - params.since = since; - } - const response = await this.easypromos._makeRequest({ - method: "GET", - path: "/coin-transactions", - params, - }); - return response.transactions || []; + ...common.methods, + getFunction() { + return this.easypromos.getCoinTransactions; }, - async _getLastTimestamp() { - return (await this.db.get("lastTimestamp")) || 0; - }, - async _setLastTimestamp(timestamp) { - await this.db.set("lastTimestamp", timestamp); - }, - }, - hooks: { - async deploy() { - try { - let page = 1; - const perPage = 50; - const fetchedTransactions = []; - const lastTimestamp = await this._getLastTimestamp(); - - while (fetchedTransactions.length < perPage) { - const transactions = await this.getCoinTransactions(page, perPage, lastTimestamp); - if (transactions.length === 0) break; - fetchedTransactions.unshift(...transactions.reverse()); - if (transactions.length < perPage) break; - page += 1; - } - - const recentTransactions = fetchedTransactions.slice(0, perPage); - for (const transaction of recentTransactions) { - this.$emit(transaction, { - id: transaction.id.toString(), - summary: `Coin transaction: ${transaction.amount} for user ${transaction.user_id}`, - ts: Date.parse(transaction.created), - }); - } - - if (recentTransactions.length > 0) { - const latestTimestamp = Date.parse(recentTransactions[0].created); - await this._setLastTimestamp(latestTimestamp); - } - } catch (error) { - this.logger.error(`Error deploying source: ${error.message}`); - } - }, - async activate() { - // No webhook setup required + getOpts() { + return { + promotionId: this.promotionId, + }; }, - async deactivate() { - // No webhook teardown required + getSummary({ + transaction, user, + }) { + return `Coin transaction: ${transaction.amount} for user ${user.email}`; }, }, - async run() { - try { - const lastTimestamp = await this._getLastTimestamp(); - let page = 1; - const perPage = 50; - const newTransactions = []; - - while (true) { - const transactions = await this.getCoinTransactions(page, perPage, lastTimestamp); - if (transactions.length === 0) break; - - for (const transaction of transactions) { - const transactionTimestamp = Date.parse(transaction.created); - if (transactionTimestamp > lastTimestamp) { - newTransactions.push(transaction); - } - } - - if (transactions.length < perPage) break; - page += 1; - } - - for (const transaction of newTransactions) { - this.$emit(transaction, { - id: transaction.id.toString(), - summary: `Coin transaction: ${transaction.amount} for user ${transaction.user_id}`, - ts: Date.parse(transaction.created), - }); - } - - if (newTransactions.length > 0) { - const latestTransaction = newTransactions.reduce((a, b) => - Date.parse(a.created) > Date.parse(b.created) - ? a - : b); - await this._setLastTimestamp(Date.parse(latestTransaction.created)); - } - } catch (error) { - this.logger.error(`Error running source: ${error.message}`); - } - }, + sampleEmit, }; diff --git a/components/easypromos/sources/new-coin-transaction/test-event.mjs b/components/easypromos/sources/new-coin-transaction/test-event.mjs new file mode 100644 index 0000000000000..ef6dc9f2de2ec --- /dev/null +++ b/components/easypromos/sources/new-coin-transaction/test-event.mjs @@ -0,0 +1,54 @@ +export default { + "transaction": { + "id": 1, + "user_id": 1, + "promotion_id": 1, + "coin_id": 1, + "coin_name": "Points", + "amount": 5, + "balance": 5, + "created": "2019-08-24T14:15:22Z", + "transaction_type": 1, + "reason": "Earned for participating in a stage", + "extra": "string" + }, + "user": { + "id": 1, + "promotion_id": 1, + "first_name": "John", + "last_name": "Smith", + "nickname": "jsmith", + "login_type": "email", + "social_id": "string", + "external_id": "string", + "status": 0, + "email": "user@example.com", + "phone": "string", + "birthday": "2019-08-24", + "created": "2019-08-24T14:15:22Z", + "avatar_img": "http://example.com", + "language": "ara", + "country": "FR", + "custom_properties": [ + { + "id": 1, + "title": "Postal code", + "ref": "postalcode", + "value": "PC98776" + } + ], + "meta_data": { + "utm_source": "instagram", + "utm_medium": "ads", + "utm_campaign": "campaign-week1", + "referral_url": "http://example.com", + "ip": "192.168.0.1", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148", + "legals": { + "terms_url": "https://a.cstmapp.com/promo_terms/919755", + "privacy_url": "https://a.cstmapp.com/policy/987543", + "accepted_cookies": 1 + } + } + } +} \ No newline at end of file diff --git a/components/easypromos/sources/new-participation/new-participation.mjs b/components/easypromos/sources/new-participation/new-participation.mjs index c0489f4b40767..3bc808c7aa328 100644 --- a/components/easypromos/sources/new-participation/new-participation.mjs +++ b/components/easypromos/sources/new-participation/new-participation.mjs @@ -1,95 +1,36 @@ -import { - axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, -} from "@pipedream/platform"; -import appName from "../../easypromos.app.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "easypromos-new-participation", name: "New Participation Submitted", - description: "Emit new event when a registered user submits a participation in the promotion. [See the documentation]()", - version: "0.0.{{ts}}", + description: "Emit new event when a registered user submits a participation in the promotion.", + version: "0.0.1", type: "source", dedupe: "unique", props: { - easypromos: { - type: "app", - app: "easypromos", - }, - promotionid: { + ...common.props, + promotionId: { propDefinition: [ - appName, - "promotionid", + common.props.easypromos, + "promotionId", ], }, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, - }, - }, }, - hooks: { - async deploy() { - const lastParticipationId = this.db.get("lastParticipationId") || 0; - const participations = await this.easypromos._makeRequest({ - method: "GET", - path: "/participations", - params: { - promotion_id: this.promotionid, - limit: 50, - sort: "id_desc", - }, - }); - - const newParticipations = participations.filter((participation) => participation.id > lastParticipationId); - - newParticipations.forEach((participation) => { - this.$emit(participation, { - id: participation.id.toString(), - summary: `New Participation by User ID ${participation.user_id}`, - ts: Date.parse(participation.created), - }); - }); - - if (participations.length > 0) { - const maxId = Math.max(...participations.map((p) => p.id)); - this.db.set("lastParticipationId", maxId); - } + methods: { + ...common.methods, + getFunction() { + return this.easypromos.getParticipations; }, - async activate() { - // No webhook setup needed for polling + getOpts() { + return { + promotionId: this.promotionId, + }; }, - async deactivate() { - // No webhook teardown needed for polling + getSummary(participation) { + return `New Participation: ${participation.id}`; }, }, - async run() { - const lastParticipationId = this.db.get("lastParticipationId") || 0; - - const participations = await this.easypromos._makeRequest({ - method: "GET", - path: "/participations", - params: { - promotion_id: this.promotionid, - limit: 50, - sort: "id_desc", - }, - }); - - const newParticipations = participations.filter((participation) => participation.id > lastParticipationId); - - newParticipations.forEach((participation) => { - this.$emit(participation, { - id: participation.id.toString(), - summary: `New Participation by User ID ${participation.user_id}`, - ts: Date.parse(participation.created) || Date.now(), - }); - }); - - if (participations.length > 0) { - const maxId = Math.max(...participations.map((p) => p.id)); - this.db.set("lastParticipationId", maxId); - } - }, + sampleEmit, }; diff --git a/components/easypromos/sources/new-participation/test-event.mjs b/components/easypromos/sources/new-participation/test-event.mjs new file mode 100644 index 0000000000000..9a483b1afc87c --- /dev/null +++ b/components/easypromos/sources/new-participation/test-event.mjs @@ -0,0 +1,19 @@ +export default { + "id": 1, + "promotion_id": 1, + "stage_id": 1, + "user_id": 1, + "created": "2019-08-24T14:15:22Z", + "ip": "192.168.0.1", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) GSA/201.0.429950705 Mobile/15E148 Safari/604.1", + "points": 786.43, + "data": [ + { + "ref": "Question1", + "title": "Pick your favorite city", + "values": [ + "Barcelona" + ] + } + ] +} \ No newline at end of file diff --git a/components/easypromos/sources/new-user/new-user.mjs b/components/easypromos/sources/new-user/new-user.mjs index 82cdfe260f17a..0ee09ecd15adc 100644 --- a/components/easypromos/sources/new-user/new-user.mjs +++ b/components/easypromos/sources/new-user/new-user.mjs @@ -1,82 +1,36 @@ -import { - axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, -} from "@pipedream/platform"; -import easypromos from "../../easypromos.app.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "easypromos-new-user", name: "New User Registration", - description: "Emits a new event when a user registers in the promotion. [See the documentation]().", - version: "0.0.{{ts}}", + description: "Emit new event when a user registers in the promotion.", + version: "0.0.1", type: "source", dedupe: "unique", props: { - easypromos, - promotionid: { + ...common.props, + promotionId: { propDefinition: [ - easypromos, - "promotionid", + common.props.easypromos, + "promotionId", ], }, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, - }, - }, }, - hooks: { - async deploy() { - const lastTs = 0; - await this.db.set("lastTs", lastTs); - const registrations = await this.easypromos.getUsers({ - params: { - promotion_id: this.promotionid, - since: lastTs, - per_page: 50, - }, - }); - for (const registration of registrations.slice(0, 50)) { - const ts = Date.parse(registration.created_at) || Date.now(); - this.$emit(registration, { - id: registration.id || ts, - summary: `New User Registration: ${registration.name}`, - ts, - }); - } - if (registrations.length > 0) { - const latestTs = Math.max(...registrations.map((r) => Date.parse(r.created_at) || 0)); - await this.db.set("lastTs", latestTs); - } + methods: { + ...common.methods, + getFunction() { + return this.easypromos.getUsers; }, - async activate() { - // Placeholder for activation logic if needed + getOpts() { + return { + promotionId: this.promotionId, + }; }, - async deactivate() { - // Placeholder for deactivation logic if needed + getSummary(user) { + return `New User Registration: ${user.email}`; }, }, - async run() { - const lastTs = await this.db.get("lastTs") || 0; - const registrations = await this.easypromos.getUsers({ - params: { - promotion_id: this.promotionid, - since: lastTs, - per_page: 50, - }, - }); - for (const registration of registrations) { - const ts = Date.parse(registration.created_at) || Date.now(); - this.$emit(registration, { - id: registration.id || ts, - summary: `New User Registration: ${registration.name}`, - ts, - }); - } - if (registrations.length > 0) { - const latestTs = Math.max(...registrations.map((r) => Date.parse(r.created_at) || 0)); - await this.db.set("lastTs", latestTs); - } - }, + sampleEmit, }; diff --git a/components/easypromos/sources/new-user/test-event.mjs b/components/easypromos/sources/new-user/test-event.mjs new file mode 100644 index 0000000000000..956839b52c54c --- /dev/null +++ b/components/easypromos/sources/new-user/test-event.mjs @@ -0,0 +1,39 @@ +export default { + "id": 1, + "promotion_id": 1, + "first_name": "John", + "last_name": "Smith", + "nickname": "jsmith", + "login_type": "email", + "social_id": "string", + "external_id": "string", + "status": 0, + "email": "user@example.com", + "phone": "string", + "birthday": "2019-08-24", + "created": "2019-08-24T14:15:22Z", + "avatar_img": "http://example.com", + "language": "ara", + "country": "FR", + "custom_properties": [ + { + "id": 1, + "title": "Postal code", + "ref": "postalcode", + "value": "PC98776" + } + ], + "meta_data": { + "utm_source": "instagram", + "utm_medium": "ads", + "utm_campaign": "campaign-week1", + "referral_url": "http://example.com", + "ip": "192.168.0.1", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148", + "legals": { + "terms_url": "https://a.cstmapp.com/promo_terms/919755", + "privacy_url": "https://a.cstmapp.com/policy/987543", + "accepted_cookies": 1 + } + } +} \ No newline at end of file From 534076219566f83c3c5a361e8b74a8ef4df4292f Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 7 Jan 2025 12:57:43 -0300 Subject: [PATCH 3/7] add info prop --- components/easypromos/sources/common/base.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/easypromos/sources/common/base.mjs b/components/easypromos/sources/common/base.mjs index 09741c6c97a42..4320a20a414f5 100644 --- a/components/easypromos/sources/common/base.mjs +++ b/components/easypromos/sources/common/base.mjs @@ -11,6 +11,11 @@ export default { intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, }, }, + info: { + type: "alert", + alertType: "info", + content: "The Easypromos API only works with \"White Label\" promotions, any other type will not appear in the list.", + }, }, methods: { _getLastId() { From 44daab44034a0025607b701bc43add26402111e8 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 7 Jan 2025 12:59:02 -0300 Subject: [PATCH 4/7] pnpm update --- pnpm-lock.yaml | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79157dbac0937..150fea3d00f38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1005,8 +1005,7 @@ importers: specifier: ^9.0.1 version: 9.0.1 - components/azure_storage: - specifiers: {} + components/azure_storage: {} components/backblaze: {} @@ -3105,7 +3104,10 @@ importers: components/easypost: {} components/easypromos: - specifiers: {} + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/easysendy: dependencies: @@ -3159,8 +3161,7 @@ importers: specifier: ^4.0.0 version: 4.0.1 - components/elevio: - specifiers: {} + components/elevio: {} components/elmah_io: dependencies: @@ -4823,8 +4824,7 @@ importers: specifier: ^1.6.0 version: 1.6.6 - components/homerun: - specifiers: {} + components/homerun: {} components/hookdeck: dependencies: @@ -8319,8 +8319,7 @@ importers: specifier: ^1.1.1 version: 1.6.6 - components/ragie: - specifiers: {} + components/ragie: {} components/railsr: {} @@ -8513,8 +8512,7 @@ importers: specifier: ^1.3.0 version: 1.6.6 - components/refiner: - specifiers: {} + components/refiner: {} components/reflect: dependencies: @@ -11049,8 +11047,7 @@ importers: specifier: ^6.2.13 version: 6.2.13 - components/typefully: - specifiers: {} + components/typefully: {} components/typless: {} @@ -11575,8 +11572,7 @@ importers: components/weworkbook: {} - components/what_are_those: - specifiers: {} + components/what_are_those: {} components/whatconverts: {} From 9ebfda53301dbbe75046b83975e5e4493e6a1b4f Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 7 Jan 2025 13:50:50 -0300 Subject: [PATCH 5/7] pnpm update --- pnpm-lock.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dbbad945299f5..6fa574750823f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1008,7 +1008,6 @@ importers: version: 9.0.1 components/azure_storage: {} - components/azure_storage: {} components/backblaze: {} @@ -24699,22 +24698,22 @@ packages: superagent@3.8.1: resolution: {integrity: sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==} engines: {node: '>= 4.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@4.1.0: resolution: {integrity: sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==} engines: {node: '>= 6.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@5.3.1: resolution: {integrity: sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==} engines: {node: '>= 7.0.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@7.1.6: resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please downgrade to v7.1.5 if you need IE/ActiveXObject support OR upgrade to v8.0.0 as we no longer support IE and published an incorrect patch version (see https://github.com/visionmedia/superagent/issues/1731) + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} From 69389c532be083e1cf356b2f606822ae01a8b3d9 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 8 Jan 2025 14:44:52 -0300 Subject: [PATCH 6/7] some adjusts --- components/easypromos/easypromos.app.mjs | 4 ++-- components/easypromos/sources/common/base.mjs | 6 ++++++ .../new-coin-transaction/new-coin-transaction.mjs | 9 --------- .../sources/new-participation/new-participation.mjs | 9 --------- components/easypromos/sources/new-user/new-user.mjs | 9 --------- 5 files changed, 8 insertions(+), 29 deletions(-) diff --git a/components/easypromos/easypromos.app.mjs b/components/easypromos/easypromos.app.mjs index d2cb20c897d49..9b199adf5ad4f 100644 --- a/components/easypromos/easypromos.app.mjs +++ b/components/easypromos/easypromos.app.mjs @@ -46,10 +46,10 @@ export default { }); return { options: items.map(({ - id: value, title, internal_ref: ref, + id, title, internal_ref: ref, }) => ({ label: ref || title, - value, + value: parseInt(id), })), context: { nextCursor: paging.next_cursor, diff --git a/components/easypromos/sources/common/base.mjs b/components/easypromos/sources/common/base.mjs index 4320a20a414f5..18730235b5cd7 100644 --- a/components/easypromos/sources/common/base.mjs +++ b/components/easypromos/sources/common/base.mjs @@ -16,6 +16,12 @@ export default { alertType: "info", content: "The Easypromos API only works with \"White Label\" promotions, any other type will not appear in the list.", }, + promotionId: { + propDefinition: [ + easypromos, + "promotionId", + ], + }, }, methods: { _getLastId() { diff --git a/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs b/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs index 16770095679fb..cedac1b2fda84 100644 --- a/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs +++ b/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs @@ -9,15 +9,6 @@ export default { version: "0.0.1", type: "source", dedupe: "unique", - props: { - ...common.props, - promotionId: { - propDefinition: [ - common.props.easypromos, - "promotionId", - ], - }, - }, methods: { ...common.methods, getFunction() { diff --git a/components/easypromos/sources/new-participation/new-participation.mjs b/components/easypromos/sources/new-participation/new-participation.mjs index 3bc808c7aa328..6333b45edf447 100644 --- a/components/easypromos/sources/new-participation/new-participation.mjs +++ b/components/easypromos/sources/new-participation/new-participation.mjs @@ -9,15 +9,6 @@ export default { version: "0.0.1", type: "source", dedupe: "unique", - props: { - ...common.props, - promotionId: { - propDefinition: [ - common.props.easypromos, - "promotionId", - ], - }, - }, methods: { ...common.methods, getFunction() { diff --git a/components/easypromos/sources/new-user/new-user.mjs b/components/easypromos/sources/new-user/new-user.mjs index 0ee09ecd15adc..ed50831382516 100644 --- a/components/easypromos/sources/new-user/new-user.mjs +++ b/components/easypromos/sources/new-user/new-user.mjs @@ -9,15 +9,6 @@ export default { version: "0.0.1", type: "source", dedupe: "unique", - props: { - ...common.props, - promotionId: { - propDefinition: [ - common.props.easypromos, - "promotionId", - ], - }, - }, methods: { ...common.methods, getFunction() { From bd03556f6e021690f55709abc9301acac4bf1626 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 9 Jan 2025 13:16:38 -0300 Subject: [PATCH 7/7] some adjusts --- components/easypromos/sources/common/base.mjs | 34 ++++++++++++------- .../new-coin-transaction.mjs | 8 +++++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/components/easypromos/sources/common/base.mjs b/components/easypromos/sources/common/base.mjs index 18730235b5cd7..3123e3876ee1b 100644 --- a/components/easypromos/sources/common/base.mjs +++ b/components/easypromos/sources/common/base.mjs @@ -1,4 +1,6 @@ -import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import { + ConfigurationError, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; import easypromos from "../../easypromos.app.mjs"; export default { @@ -35,19 +37,27 @@ export default { }, async emitEvent(maxResults = false) { const lastId = this._getLastId(); + let responseArray = []; - const response = this.easypromos.paginate({ - fn: this.getFunction(), - ...this.getOpts(), - params: { - order: "created_desc", - }, - }); + try { + const response = this.easypromos.paginate({ + fn: this.getFunction(), + ...this.getOpts(), + params: { + order: "created_desc", + }, + }); - let responseArray = []; - for await (const item of response) { - if (item.id <= lastId) break; - responseArray.push(item); + for await (const item of response) { + if (item.id <= lastId) break; + responseArray.push(item); + } + } catch (err) { + console.log(err); + if (err?.response?.data?.code === 0) { + throw new ConfigurationError("You can only use this trigger with promotions that have the 'Virtual Coins' feature enabled."); + } + throw new ConfigurationError(err); } if (responseArray.length) { diff --git a/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs b/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs index cedac1b2fda84..2f0f305dccb2b 100644 --- a/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs +++ b/components/easypromos/sources/new-coin-transaction/new-coin-transaction.mjs @@ -9,6 +9,14 @@ export default { version: "0.0.1", type: "source", dedupe: "unique", + props: { + ...common.props, + info2: { + type: "alert", + alertType: "warning", + content: "You can only use this trigger with promotions that have the 'Virtual Coins' feature enabled.", + }, + }, methods: { ...common.methods, getFunction() {