From 6d47284d63303033cc67bd5fafc57f08d4629f2f Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 23 Apr 2025 14:21:17 -0300 Subject: [PATCH 1/9] peerdom init --- .../assign-member-to-role.mjs | 34 +++++ .../actions/create-role/create-role.mjs | 50 +++++++ .../actions/update-role/update-role.mjs | 65 +++++++++ components/peerdom/package.json | 2 +- components/peerdom/peerdom.app.mjs | 126 +++++++++++++++++- .../peerdom/sources/new-member/new-member.mjs | 81 +++++++++++ .../peerdom/sources/new-role/new-role.mjs | 65 +++++++++ 7 files changed, 417 insertions(+), 6 deletions(-) create mode 100644 components/peerdom/actions/assign-member-to-role/assign-member-to-role.mjs create mode 100644 components/peerdom/actions/create-role/create-role.mjs create mode 100644 components/peerdom/actions/update-role/update-role.mjs create mode 100644 components/peerdom/sources/new-member/new-member.mjs create mode 100644 components/peerdom/sources/new-role/new-role.mjs diff --git a/components/peerdom/actions/assign-member-to-role/assign-member-to-role.mjs b/components/peerdom/actions/assign-member-to-role/assign-member-to-role.mjs new file mode 100644 index 0000000000000..18f7029531a3b --- /dev/null +++ b/components/peerdom/actions/assign-member-to-role/assign-member-to-role.mjs @@ -0,0 +1,34 @@ +import peerdom from "../../peerdom.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "peerdom-assign-member-to-role", + name: "Assign Member to Role", + description: "Assigns a member to a role within a circle using the Peerdom API. [See the documentation](https://api.peerdom.org/v1/docs)", + version: "0.0.1", + type: "action", + props: { + peerdom, + roleId: { + propDefinition: [ + peerdom, + "roleId", + ], + }, + memberId: { + propDefinition: [ + peerdom, + "memberId", + ], + }, + }, + async run({ $ }) { + const response = await this.peerdom.assignMemberToRole({ + roleId: this.roleId, + memberId: this.memberId, + }); + + $.export("$summary", `Successfully assigned member with ID ${this.memberId} to role with ID ${this.roleId}`); + return response; + }, +}; diff --git a/components/peerdom/actions/create-role/create-role.mjs b/components/peerdom/actions/create-role/create-role.mjs new file mode 100644 index 0000000000000..220588f38fe70 --- /dev/null +++ b/components/peerdom/actions/create-role/create-role.mjs @@ -0,0 +1,50 @@ +import peerdom from "../../peerdom.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "peerdom-create-role", + name: "Create Role", + description: "Create a new role within a specified circle. [See the documentation](https://api.peerdom.org/v1/docs)", + version: "0.0.{{ts}}", + type: "action", + props: { + peerdom, + circleId: { + propDefinition: [ + peerdom, + "circleId", + ], + }, + roleName: { + propDefinition: [ + peerdom, + "roleName", + ], + }, + description: { + propDefinition: [ + peerdom, + "description", + ], + optional: true, + }, + linkedDomains: { + propDefinition: [ + peerdom, + "linkedDomains", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.peerdom.createRole({ + circleId: this.circleId, + roleName: this.roleName, + description: this.description, + linkedDomains: this.linkedDomains, + }); + + $.export("$summary", `Successfully created role: ${response.name}`); + return response; + }, +}; diff --git a/components/peerdom/actions/update-role/update-role.mjs b/components/peerdom/actions/update-role/update-role.mjs new file mode 100644 index 0000000000000..93749e3914365 --- /dev/null +++ b/components/peerdom/actions/update-role/update-role.mjs @@ -0,0 +1,65 @@ +import peerdom from "../../peerdom.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "peerdom-update-role", + name: "Update Role", + description: "Update an existing role's attributes such as name, description, or linked domains. [See the documentation](https://api.peerdom.org/v1/docs)", + version: "0.0.1", + type: "action", + props: { + peerdom, + roleId: { + propDefinition: [ + peerdom, + "roleId", + ], + }, + roleName: { + propDefinition: [ + peerdom, + "roleName", + ], + optional: true, + }, + description: { + propDefinition: [ + peerdom, + "description", + ], + optional: true, + }, + linkedDomains: { + propDefinition: [ + peerdom, + "linkedDomains", + ], + optional: true, + }, + }, + async run({ $ }) { + const data = { + ...(this.roleName && { + name: this.roleName, + }), + ...(this.description && { + customFields: { + Description: this.description, + }, + }), + ...(this.linkedDomains && { + customFields: { + LinkedDomains: this.linkedDomains, + }, + }), + }; + + const response = await this.peerdom.updateRole({ + roleId: this.roleId, + ...data, + }); + + $.export("$summary", `Successfully updated role with ID ${this.roleId}`); + return response; + }, +}; diff --git a/components/peerdom/package.json b/components/peerdom/package.json index 062fe5f18d560..5f435d972440d 100644 --- a/components/peerdom/package.json +++ b/components/peerdom/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/peerdom/peerdom.app.mjs b/components/peerdom/peerdom.app.mjs index edc5aa5f4c81a..f59173bfefe0a 100644 --- a/components/peerdom/peerdom.app.mjs +++ b/components/peerdom/peerdom.app.mjs @@ -1,11 +1,127 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "peerdom", - propDefinitions: {}, + propDefinitions: { + circleId: { + type: "string", + label: "Circle ID", + description: "The ID of the circle (organization)", + }, + roleId: { + type: "string", + label: "Role ID", + description: "The ID of the role", + }, + memberId: { + type: "string", + label: "Member ID", + description: "The ID of the member", + }, + roleName: { + type: "string", + label: "Role Name", + description: "The name of the role to be created", + }, + description: { + type: "string", + label: "Description", + description: "Optional description for the role", + optional: true, + }, + linkedDomains: { + type: "string[]", + label: "Linked Domains", + description: "Optional linked domains for the role", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.peerdom.org/v1"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, method = "GET", path = "/", headers, ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + "X-Api-Key": this.$auth.api_key, + }, + }); + }, + async createRole({ + circleId, roleName, description, linkedDomains, + }) { + const data = { + name: roleName, + parentId: circleId, + ...(description && { + customFields: { + Description: description, + }, + }), + ...(linkedDomains && { + customFields: { + LinkedDomains: linkedDomains, + }, + }), + }; + return this._makeRequest({ + method: "POST", + path: "/roles", + data, + }); + }, + async assignMemberToRole({ + roleId, memberId, + }) { + const data = { + roleId, + memberId, + }; + return this._makeRequest({ + method: "POST", + path: `/roles/${roleId}/assign`, + data, + }); + }, + async updateRole({ + roleId, name, description, linkedDomains, + }) { + const data = { + ...(name && { + name, + }), + ...(description && { + customFields: { + Description: description, + }, + }), + ...(linkedDomains && { + customFields: { + LinkedDomains: linkedDomains, + }, + }), + }; + return this._makeRequest({ + method: "PATCH", + path: `/roles/${roleId}`, + data, + }); + }, + async emitNewRoleEvent({ circleId }) { + // Logic to listen for a new role event + // and emit the event + }, + async emitNewMemberEvent({ circleId }) { + // Logic to listen for a new member event + // and emit the event }, }, -}; \ No newline at end of file +}; diff --git a/components/peerdom/sources/new-member/new-member.mjs b/components/peerdom/sources/new-member/new-member.mjs new file mode 100644 index 0000000000000..e50532fec3db8 --- /dev/null +++ b/components/peerdom/sources/new-member/new-member.mjs @@ -0,0 +1,81 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import peerdom from "../../peerdom.app.mjs"; + +export default { + key: "peerdom-new-member", + name: "New Member Added", + description: "Emit new event when a new member is added to a circle or assigned to a role. [See the documentation](https://api.peerdom.org/v1/docs)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + peerdom, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + circleId: { + propDefinition: [ + peerdom, + "circleId", + ], + }, + }, + methods: { + _getLastTimestamp() { + return this.db.get("lastTimestamp") || 0; + }, + _setLastTimestamp(ts) { + this.db.set("lastTimestamp", ts); + }, + async _getNewMembers(circleId, since) { + return this.peerdom._makeRequest({ + path: `/circles/${circleId}/members?since=${since}`, + }); + }, + }, + hooks: { + async deploy() { + const currentTimestamp = Date.now(); + const newMembers = await this._getNewMembers(this.circleId, 0); + + newMembers.slice(0, 50).forEach((member) => { + this.$emit(member, { + id: member.id, + summary: `New Member Added: ${member.name}`, + ts: Date.parse(member.createdAt), + }); + }); + + this._setLastTimestamp(currentTimestamp); + }, + async activate() { + // No activation needed for polling + }, + async deactivate() { + // No deactivation needed for polling + }, + }, + async run() { + const lastTimestamp = this._getLastTimestamp(); + const newMembers = await this._getNewMembers(this.circleId, lastTimestamp); + + newMembers.forEach((member) => { + this.$emit(member, { + id: member.id, + summary: `New Member Added: ${member.name}`, + ts: Date.parse(member.createdAt), + }); + }); + + if (newMembers.length) { + const latestTimestamp = Date.parse(newMembers[newMembers.length - 1].createdAt); + this._setLastTimestamp(latestTimestamp); + } + }, +}; diff --git a/components/peerdom/sources/new-role/new-role.mjs b/components/peerdom/sources/new-role/new-role.mjs new file mode 100644 index 0000000000000..ad4a33862c6a7 --- /dev/null +++ b/components/peerdom/sources/new-role/new-role.mjs @@ -0,0 +1,65 @@ +import { axios } from "@pipedream/platform"; +import peerdom from "../../peerdom.app.mjs"; + +export default { + key: "peerdom-new-role", + name: "New Role Created", + description: "Emit new event when a new role is created in a circle. [See the documentation](https://api.peerdom.org/v1/docs)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + peerdom, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: 3600, + }, + }, + circleId: { + propDefinition: [ + peerdom, + "circleId", + ], + }, + }, + methods: { + async getRoles(circleId) { + return this.peerdom._makeRequest({ + method: "GET", + path: `/circles/${circleId}/roles`, + }); + }, + async emitRoleEvents() { + const storedRoleIds = this.db.get("roleIds") || []; + const roles = await this.getRoles(this.circleId); + const newRoles = roles.filter((role) => !storedRoleIds.includes(role.id)); + + for (const role of newRoles) { + this.$emit(role, { + id: role.id, + summary: `New Role Created: ${role.name}`, + ts: new Date(role.createdAt).getTime(), + }); + } + + const allRoleIds = roles.map((role) => role.id); + this.db.set("roleIds", allRoleIds); + }, + }, + hooks: { + async deploy() { + await this.emitRoleEvents(); + }, + async activate() { + // Add code for activation if needed + }, + async deactivate() { + // Add code for deactivation if needed + }, + }, + async run() { + await this.emitRoleEvents(); + }, +}; From 7aa5e109f2b84395874ec556d9f5317980c670ea Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 23 Apr 2025 17:50:15 -0300 Subject: [PATCH 2/9] [Components] peerdom #16400 Sources - New Role - New Member Actions - Create Role - Assign Member To Role - Update Role --- .../assign-member-to-role.mjs | 29 ++- .../actions/create-role/create-role.mjs | 82 ++++-- .../actions/update-role/update-role.mjs | 90 ++++--- components/peerdom/common/constants.mjs | 6 + components/peerdom/common/utils.mjs | 24 ++ components/peerdom/package.json | 5 +- components/peerdom/peerdom.app.mjs | 234 +++++++++++++----- components/peerdom/sources/common/base.mjs | 58 +++++ .../peerdom/sources/new-member/new-member.mjs | 81 +----- .../peerdom/sources/new-member/test-event.mjs | 19 ++ .../peerdom/sources/new-role/new-role.mjs | 63 +---- .../peerdom/sources/new-role/test-event.mjs | 50 ++++ 12 files changed, 502 insertions(+), 239 deletions(-) create mode 100644 components/peerdom/common/constants.mjs create mode 100644 components/peerdom/common/utils.mjs create mode 100644 components/peerdom/sources/common/base.mjs create mode 100644 components/peerdom/sources/new-member/test-event.mjs create mode 100644 components/peerdom/sources/new-role/test-event.mjs diff --git a/components/peerdom/actions/assign-member-to-role/assign-member-to-role.mjs b/components/peerdom/actions/assign-member-to-role/assign-member-to-role.mjs index 18f7029531a3b..c90374a89697c 100644 --- a/components/peerdom/actions/assign-member-to-role/assign-member-to-role.mjs +++ b/components/peerdom/actions/assign-member-to-role/assign-member-to-role.mjs @@ -1,5 +1,4 @@ import peerdom from "../../peerdom.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "peerdom-assign-member-to-role", @@ -21,11 +20,37 @@ export default { "memberId", ], }, + percentage: { + type: "integer", + label: "Percentage", + description: "The percentage of the role assigned to the member", + optional: true, + min: 0, + max: 100, + }, + focus: { + type: "string", + label: "Focus", + description: "The focus of the role assigned to the member", + optional: true, + }, + electedUntil: { + type: "string", + label: "Elected Until", + description: "The date until which the member is elected to the role (YYYY-MM-DD)", + optional: true, + }, }, async run({ $ }) { const response = await this.peerdom.assignMemberToRole({ + $, roleId: this.roleId, - memberId: this.memberId, + data: { + peerId: this.memberId, + percentage: this.percentage, + focus: this.focus, + electedUntil: this.electedUntil, + }, }); $.export("$summary", `Successfully assigned member with ID ${this.memberId} to role with ID ${this.roleId}`); diff --git a/components/peerdom/actions/create-role/create-role.mjs b/components/peerdom/actions/create-role/create-role.mjs index 220588f38fe70..52d8fb522aaa9 100644 --- a/components/peerdom/actions/create-role/create-role.mjs +++ b/components/peerdom/actions/create-role/create-role.mjs @@ -1,50 +1,96 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { parseObject } from "../../common/utils.mjs"; import peerdom from "../../peerdom.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "peerdom-create-role", name: "Create Role", description: "Create a new role within a specified circle. [See the documentation](https://api.peerdom.org/v1/docs)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { peerdom, - circleId: { + name: { propDefinition: [ peerdom, - "circleId", + "name", ], }, - roleName: { + mapId: { propDefinition: [ peerdom, - "roleName", + "mapId", ], }, - description: { + parentId: { propDefinition: [ peerdom, - "description", + "groupId", + ], + }, + electable: { + propDefinition: [ + peerdom, + "electable", + ], + optional: true, + }, + external: { + propDefinition: [ + peerdom, + "external", + ], + optional: true, + }, + color: { + propDefinition: [ + peerdom, + "color", ], optional: true, }, - linkedDomains: { + shape: { propDefinition: [ peerdom, - "linkedDomains", + "shape", + ], + optional: true, + }, + customFields: { + propDefinition: [ + peerdom, + "customFields", + ], + optional: true, + }, + groupEmail: { + propDefinition: [ + peerdom, + "groupEmail", ], optional: true, }, }, async run({ $ }) { - const response = await this.peerdom.createRole({ - circleId: this.circleId, - roleName: this.roleName, - description: this.description, - linkedDomains: this.linkedDomains, - }); + try { + const { + peerdom, + customFields, + ...data + } = this; + + const response = await peerdom.createRole({ + $, + data: { + ...data, + customFields: parseObject(customFields), + }, + }); - $.export("$summary", `Successfully created role: ${response.name}`); - return response; + $.export("$summary", `Successfully created role: ${response.name}`); + return response; + } catch ({ response }) { + throw new ConfigurationError(response.data.message); + } }, }; diff --git a/components/peerdom/actions/update-role/update-role.mjs b/components/peerdom/actions/update-role/update-role.mjs index 93749e3914365..0b26a17dab159 100644 --- a/components/peerdom/actions/update-role/update-role.mjs +++ b/components/peerdom/actions/update-role/update-role.mjs @@ -1,5 +1,6 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { parseObject } from "../../common/utils.mjs"; import peerdom from "../../peerdom.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "peerdom-update-role", @@ -15,51 +16,84 @@ export default { "roleId", ], }, - roleName: { + name: { propDefinition: [ peerdom, - "roleName", + "name", ], optional: true, }, - description: { + parentId: { propDefinition: [ peerdom, - "description", + "groupId", + ], + }, + electable: { + propDefinition: [ + peerdom, + "electable", + ], + optional: true, + }, + external: { + propDefinition: [ + peerdom, + "external", + ], + optional: true, + }, + color: { + propDefinition: [ + peerdom, + "color", ], optional: true, }, - linkedDomains: { + shape: { propDefinition: [ peerdom, - "linkedDomains", + "shape", + ], + optional: true, + }, + customFields: { + propDefinition: [ + peerdom, + "customFields", + ], + optional: true, + }, + groupEmail: { + propDefinition: [ + peerdom, + "groupEmail", ], optional: true, }, }, async run({ $ }) { - const data = { - ...(this.roleName && { - name: this.roleName, - }), - ...(this.description && { - customFields: { - Description: this.description, - }, - }), - ...(this.linkedDomains && { - customFields: { - LinkedDomains: this.linkedDomains, - }, - }), - }; + try { + const { + peerdom, + roleId, + customFields, + ...data + } = this; - const response = await this.peerdom.updateRole({ - roleId: this.roleId, - ...data, - }); + const response = await peerdom.updateRole({ + $, + roleId, + data: { + ...data, + customFields: parseObject(customFields), + }, + }); - $.export("$summary", `Successfully updated role with ID ${this.roleId}`); - return response; + $.export("$summary", `Successfully updated role with ID ${this.roleId}`); + return response; + } catch ({ response }) { + throw new ConfigurationError(response.data.message); + } }, }; diff --git a/components/peerdom/common/constants.mjs b/components/peerdom/common/constants.mjs new file mode 100644 index 0000000000000..fd3d1ee72fa1d --- /dev/null +++ b/components/peerdom/common/constants.mjs @@ -0,0 +1,6 @@ +export const LIMIT = 100; + +export const SHAPE_OPTIONS = [ + "circle", + "hexagon", +]; diff --git a/components/peerdom/common/utils.mjs b/components/peerdom/common/utils.mjs new file mode 100644 index 0000000000000..dcc9cc61f6f41 --- /dev/null +++ b/components/peerdom/common/utils.mjs @@ -0,0 +1,24 @@ +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; diff --git a/components/peerdom/package.json b/components/peerdom/package.json index 5f435d972440d..dca78856fc590 100644 --- a/components/peerdom/package.json +++ b/components/peerdom/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/peerdom", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Peerdom Components", "main": "peerdom.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/peerdom/peerdom.app.mjs b/components/peerdom/peerdom.app.mjs index f59173bfefe0a..ac7d2a23a618a 100644 --- a/components/peerdom/peerdom.app.mjs +++ b/components/peerdom/peerdom.app.mjs @@ -1,23 +1,115 @@ import { axios } from "@pipedream/platform"; +import { + LIMIT, SHAPE_OPTIONS, +} from "./common/constants.mjs"; export default { type: "app", app: "peerdom", propDefinitions: { - circleId: { + groupId: { type: "string", - label: "Circle ID", - description: "The ID of the circle (organization)", + label: "Parent ID", + description: "The Id should be a valid group ID.", + async options() { + const data = await this.listGroups(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + mapId: { + type: "string", + label: "Map ID", + description: "The ID of the map", + async options() { + const data = await this.listMaps(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, }, roleId: { type: "string", label: "Role ID", - description: "The ID of the role", + description: "ID of role to update.", + async options() { + const data = await this.listRoles(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, }, memberId: { type: "string", label: "Member ID", description: "The ID of the member", + async options() { + const data = await this.listPeers(); + + return data.map(({ + id: value, firstName, lastName, + }) => ({ + label: `${firstName} ${lastName}`, + value, + })); + }, + }, + name: { + type: "string", + label: "Name", + description: "The name of the role to be created", + }, + electable: { + type: "boolean", + label: "Electable", + description: "Whether the role is electable or not", + }, + external: { + type: "boolean", + label: "External", + description: "Set external to true if node is outside of the organization", + }, + color: { + type: "string", + label: "Color", + description: "The choice of color for the node in hexa decimal string format", + }, + shape: { + type: "string", + label: "Shape", + description: "Specifies the shape of the node that determines the visual representation of the node within the interface", + options: SHAPE_OPTIONS, + optional: true, + }, + customFields: { + type: "object", + label: "Custom Fields", + description: "The custom fields for a group/role. You can add the properties from the predefined custom fields. [See the documentation](https://api.peerdom.org/v1/docs#tag/Roles/operation/postRole) for further details.", + }, + groupEmail: { + type: "string", + label: "Group Email", + description: "Email for node(group/role) communication", + optional: true, + }, + + circleId: { + type: "string", + label: "Circle ID", + description: "The ID of the circle (organization)", }, roleName: { type: "string", @@ -41,87 +133,95 @@ export default { _baseUrl() { return "https://api.peerdom.org/v1"; }, - async _makeRequest(opts = {}) { - const { - $ = this, method = "GET", path = "/", headers, ...otherOpts - } = opts; + _headers() { + return { + "content-type": "application/json", + "x-api-key": `${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { return axios($, { - ...otherOpts, - method, url: this._baseUrl() + path, - headers: { - ...headers, - "X-Api-Key": this.$auth.api_key, - }, + headers: this._headers(), + ...opts, }); }, - async createRole({ - circleId, roleName, description, linkedDomains, - }) { - const data = { - name: roleName, - parentId: circleId, - ...(description && { - customFields: { - Description: description, - }, - }), - ...(linkedDomains && { - customFields: { - LinkedDomains: linkedDomains, - }, - }), - }; + listGroups(opts = {}) { + return this._makeRequest({ + path: "/groups", + ...opts, + }); + }, + listMaps(opts = {}) { + return this._makeRequest({ + path: "/maps", + ...opts, + }); + }, + listRoles(opts = {}) { return this._makeRequest({ - method: "POST", path: "/roles", - data, + ...opts, }); }, - async assignMemberToRole({ - roleId, memberId, - }) { - const data = { - roleId, - memberId, - }; + listPeers(opts = {}) { + return this._makeRequest({ + path: "/peers", + ...opts, + }); + }, + createRole(opts = {}) { return this._makeRequest({ method: "POST", - path: `/roles/${roleId}/assign`, - data, + path: "/roles", + ...opts, }); }, - async updateRole({ - roleId, name, description, linkedDomains, + updateRole({ + roleId, ...opts }) { - const data = { - ...(name && { - name, - }), - ...(description && { - customFields: { - Description: description, - }, - }), - ...(linkedDomains && { - customFields: { - LinkedDomains: linkedDomains, - }, - }), - }; return this._makeRequest({ - method: "PATCH", + method: "PUT", path: `/roles/${roleId}`, - data, + ...opts, }); }, - async emitNewRoleEvent({ circleId }) { - // Logic to listen for a new role event - // and emit the event + async assignMemberToRole({ + roleId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/roles/${roleId}/peers`, + ...opts, + }); }, - async emitNewMemberEvent({ circleId }) { - // Logic to listen for a new member event - // and emit the event + async *paginate({ + fn, params = {}, maxResults = null, ...opts + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params.limit = LIMIT; + params.offset = (LIMIT * page++) + 1; + const data = await fn({ + params, + ...opts, + }); + for (const d of data) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = data.length; + + } while (hasMore); }, }, }; diff --git a/components/peerdom/sources/common/base.mjs b/components/peerdom/sources/common/base.mjs new file mode 100644 index 0000000000000..32742a8e4cae5 --- /dev/null +++ b/components/peerdom/sources/common/base.mjs @@ -0,0 +1,58 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import peerdom from "../../peerdom.app.mjs"; + +export default { + props: { + peerdom, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastId() { + return this.db.get("lastId") || ""; + }, + _setLastId(lastId) { + this.db.set("lastId", lastId); + }, + async emitEvent(maxResults = false) { + const lastId = this._getLastId(); + const fn = this.getFunction(); + + const response = await fn(); + + let responseArray = []; + for (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, + summary: this.getSummary(item), + ts: Date.now(), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/peerdom/sources/new-member/new-member.mjs b/components/peerdom/sources/new-member/new-member.mjs index e50532fec3db8..ee90181beaa1c 100644 --- a/components/peerdom/sources/new-member/new-member.mjs +++ b/components/peerdom/sources/new-member/new-member.mjs @@ -1,81 +1,22 @@ -import { - axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, -} from "@pipedream/platform"; -import peerdom from "../../peerdom.app.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "peerdom-new-member", name: "New Member Added", - description: "Emit new event when a new member is added to a circle or assigned to a role. [See the documentation](https://api.peerdom.org/v1/docs)", - version: "0.0.{{ts}}", + description: "Emit new event when a new member is added to a group. [See the documentation](https://api.peerdom.org/v1/docs#tag/Peers/operation/getPeers)", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - peerdom, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, - }, - }, - circleId: { - propDefinition: [ - peerdom, - "circleId", - ], - }, - }, methods: { - _getLastTimestamp() { - return this.db.get("lastTimestamp") || 0; - }, - _setLastTimestamp(ts) { - this.db.set("lastTimestamp", ts); + ...common.methods, + getFunction() { + return this.peerdom.listPeers; }, - async _getNewMembers(circleId, since) { - return this.peerdom._makeRequest({ - path: `/circles/${circleId}/members?since=${since}`, - }); + getSummary(item) { + return `New Member: ${item.firstName} ${item.lastName}`; }, }, - hooks: { - async deploy() { - const currentTimestamp = Date.now(); - const newMembers = await this._getNewMembers(this.circleId, 0); - - newMembers.slice(0, 50).forEach((member) => { - this.$emit(member, { - id: member.id, - summary: `New Member Added: ${member.name}`, - ts: Date.parse(member.createdAt), - }); - }); - - this._setLastTimestamp(currentTimestamp); - }, - async activate() { - // No activation needed for polling - }, - async deactivate() { - // No deactivation needed for polling - }, - }, - async run() { - const lastTimestamp = this._getLastTimestamp(); - const newMembers = await this._getNewMembers(this.circleId, lastTimestamp); - - newMembers.forEach((member) => { - this.$emit(member, { - id: member.id, - summary: `New Member Added: ${member.name}`, - ts: Date.parse(member.createdAt), - }); - }); - - if (newMembers.length) { - const latestTimestamp = Date.parse(newMembers[newMembers.length - 1].createdAt); - this._setLastTimestamp(latestTimestamp); - } - }, + sampleEmit, }; diff --git a/components/peerdom/sources/new-member/test-event.mjs b/components/peerdom/sources/new-member/test-event.mjs new file mode 100644 index 0000000000000..0ad7248f09309 --- /dev/null +++ b/components/peerdom/sources/new-member/test-event.mjs @@ -0,0 +1,19 @@ +export default { + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "firstName": "string", + "lastName": "string", + "nickName": "string", + "slug": "string", + "avatarUrl": "string", + "birthdate": "2019-08-24T14:15:22Z", + "contribution": 0, + "contributionUnit": "string", + "customFields": [ + { + "name": "string", + "values": [ + "string" + ] + } + ] +} \ No newline at end of file diff --git a/components/peerdom/sources/new-role/new-role.mjs b/components/peerdom/sources/new-role/new-role.mjs index ad4a33862c6a7..48616c2254f7c 100644 --- a/components/peerdom/sources/new-role/new-role.mjs +++ b/components/peerdom/sources/new-role/new-role.mjs @@ -1,65 +1,22 @@ -import { axios } from "@pipedream/platform"; -import peerdom from "../../peerdom.app.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "peerdom-new-role", name: "New Role Created", description: "Emit new event when a new role is created in a circle. [See the documentation](https://api.peerdom.org/v1/docs)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - peerdom, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: 3600, - }, - }, - circleId: { - propDefinition: [ - peerdom, - "circleId", - ], - }, - }, methods: { - async getRoles(circleId) { - return this.peerdom._makeRequest({ - method: "GET", - path: `/circles/${circleId}/roles`, - }); - }, - async emitRoleEvents() { - const storedRoleIds = this.db.get("roleIds") || []; - const roles = await this.getRoles(this.circleId); - const newRoles = roles.filter((role) => !storedRoleIds.includes(role.id)); - - for (const role of newRoles) { - this.$emit(role, { - id: role.id, - summary: `New Role Created: ${role.name}`, - ts: new Date(role.createdAt).getTime(), - }); - } - - const allRoleIds = roles.map((role) => role.id); - this.db.set("roleIds", allRoleIds); - }, - }, - hooks: { - async deploy() { - await this.emitRoleEvents(); + ...common.methods, + getFunction() { + return this.peerdom.listRoles; }, - async activate() { - // Add code for activation if needed + getSummary(item) { + return `New Role Created: ${item.name}`; }, - async deactivate() { - // Add code for deactivation if needed - }, - }, - async run() { - await this.emitRoleEvents(); }, + sampleEmit, }; diff --git a/components/peerdom/sources/new-role/test-event.mjs b/components/peerdom/sources/new-role/test-event.mjs new file mode 100644 index 0000000000000..86faf17132eeb --- /dev/null +++ b/components/peerdom/sources/new-role/test-event.mjs @@ -0,0 +1,50 @@ +export default { + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "color": "string", + "createdAt": "2019-08-24T14:15:22Z", + "parentId": "70850378-7d3c-4f45-91b7-942d4dfbbd43", + "slug": "string", + "external": true, + "electable": true, + "salaryLevel": "string", + "mirrored": true, + "shape": "string", + "customFields": [ + { + "name": "string", + "values": [ + "string" + ] + } + ], + "holders": [ + { + "peerId": "60a07d40-746d-414c-b70b-908ca16e7459", + "contribution": 0, + "contributionUnit": "string" + } + ], + "goals": [ + { + "endedAt": "2019-05-17", + "startedAt": "2019-05-17", + "archived": true, + "data": { + "order": "string", + "name": "string", + "isComplete": true, + "subgoals": [ + { + "name": "string", + "percentComplete": 0, + "isComplete": true, + "amountComplete": 0, + "targetAmount": 0 + } + ] + } + } + ], + "groupEmail": "string" +} \ No newline at end of file From 0f6bcc7e035d957450a73f0f2a03cbc94c131f89 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 23 Apr 2025 17:53:04 -0300 Subject: [PATCH 3/9] pnpm update --- pnpm-lock.yaml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5b20f827b00b..a4afe03d01a15 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9493,7 +9493,11 @@ importers: specifier: ^2.0.0 version: 2.0.0 - components/peerdom: {} + components/peerdom: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/pendo: {} @@ -15273,7 +15277,7 @@ importers: version: 3.1.7 ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2) + version: 29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2) tsup: specifier: ^8.3.6 version: 8.3.6(@microsoft/api-extractor@7.47.12(@types/node@20.17.30))(jiti@1.21.6)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.6.1) @@ -15310,7 +15314,7 @@ importers: version: 3.1.0 jest: specifier: ^29.1.2 - version: 29.7.0(@types/node@20.17.6)(babel-plugin-macros@3.1.0) + version: 29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0) type-fest: specifier: ^4.15.0 version: 4.27.0 @@ -47741,7 +47745,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2): + ts-jest@29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -47755,10 +47759,10 @@ snapshots: typescript: 5.7.2 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.26.0 + '@babel/core': 8.0.0-alpha.13 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.0) + babel-jest: 29.7.0(@babel/core@8.0.0-alpha.13) esbuild: 0.24.2 ts-jest@29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(jest@29.7.0(@types/node@20.17.6)(babel-plugin-macros@3.1.0))(typescript@5.6.3): From dd5d498624d79c66a6aad7e3aba307a20d24499c Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 23 Apr 2025 17:54:43 -0300 Subject: [PATCH 4/9] pnpm update --- pnpm-lock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4afe03d01a15..5fdb37d556159 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15277,7 +15277,7 @@ importers: version: 3.1.7 ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2) tsup: specifier: ^8.3.6 version: 8.3.6(@microsoft/api-extractor@7.47.12(@types/node@20.17.30))(jiti@1.21.6)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.6.1) @@ -15314,7 +15314,7 @@ importers: version: 3.1.0 jest: specifier: ^29.1.2 - version: 29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0) + version: 29.7.0(@types/node@20.17.6)(babel-plugin-macros@3.1.0) type-fest: specifier: ^4.15.0 version: 4.27.0 @@ -47745,7 +47745,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2): + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -47759,10 +47759,10 @@ snapshots: typescript: 5.7.2 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 8.0.0-alpha.13 + '@babel/core': 7.26.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@8.0.0-alpha.13) + babel-jest: 29.7.0(@babel/core@7.26.0) esbuild: 0.24.2 ts-jest@29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(jest@29.7.0(@types/node@20.17.6)(babel-plugin-macros@3.1.0))(typescript@5.6.3): From be483b3a08f4f3ce29c2f42849f2be197e52b9e6 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 24 Apr 2025 15:56:26 -0300 Subject: [PATCH 5/9] Update components/peerdom/peerdom.app.mjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Guilherme Falcão <48412907+GTFalcao@users.noreply.github.com> --- components/peerdom/peerdom.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/peerdom/peerdom.app.mjs b/components/peerdom/peerdom.app.mjs index ac7d2a23a618a..c89663ea0295c 100644 --- a/components/peerdom/peerdom.app.mjs +++ b/components/peerdom/peerdom.app.mjs @@ -10,7 +10,7 @@ export default { groupId: { type: "string", label: "Parent ID", - description: "The Id should be a valid group ID.", + description: "The ID should be a valid group ID.", async options() { const data = await this.listGroups(); From d2ec3eb2482e87ecaf1c55a7b14f6502ea38407f Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 24 Apr 2025 15:56:40 -0300 Subject: [PATCH 6/9] Update components/peerdom/peerdom.app.mjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Guilherme Falcão <48412907+GTFalcao@users.noreply.github.com> --- components/peerdom/peerdom.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/peerdom/peerdom.app.mjs b/components/peerdom/peerdom.app.mjs index c89663ea0295c..b8dd57278bf29 100644 --- a/components/peerdom/peerdom.app.mjs +++ b/components/peerdom/peerdom.app.mjs @@ -80,7 +80,7 @@ export default { external: { type: "boolean", label: "External", - description: "Set external to true if node is outside of the organization", + description: "Set to `true` if node is outside of the organization", }, color: { type: "string", From 1b49aed37c4133c757cc55ca3a7d4e14e702afa6 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 24 Apr 2025 15:56:50 -0300 Subject: [PATCH 7/9] Update components/peerdom/peerdom.app.mjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Guilherme Falcão <48412907+GTFalcao@users.noreply.github.com> --- components/peerdom/peerdom.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/peerdom/peerdom.app.mjs b/components/peerdom/peerdom.app.mjs index b8dd57278bf29..837feb1b9c09b 100644 --- a/components/peerdom/peerdom.app.mjs +++ b/components/peerdom/peerdom.app.mjs @@ -102,7 +102,7 @@ export default { groupEmail: { type: "string", label: "Group Email", - description: "Email for node(group/role) communication", + description: "Email for node (group/role) communication", optional: true, }, From 43e4295c34450acab5a498d90043ba53c3ece969 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 24 Apr 2025 15:56:58 -0300 Subject: [PATCH 8/9] Update components/peerdom/peerdom.app.mjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Guilherme Falcão <48412907+GTFalcao@users.noreply.github.com> --- components/peerdom/peerdom.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/peerdom/peerdom.app.mjs b/components/peerdom/peerdom.app.mjs index 837feb1b9c09b..a638fda5a2569 100644 --- a/components/peerdom/peerdom.app.mjs +++ b/components/peerdom/peerdom.app.mjs @@ -85,7 +85,7 @@ export default { color: { type: "string", label: "Color", - description: "The choice of color for the node in hexa decimal string format", + description: "The choice of color for the node in hexadecimal string format, e.g. `#f3a935`", }, shape: { type: "string", From 9021d2bd6335be0b4d075be1bff018b048e571b1 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 28 Apr 2025 17:24:19 -0300 Subject: [PATCH 9/9] some adjusts --- components/peerdom/sources/common/base.mjs | 7 +++++-- components/peerdom/sources/new-member/new-member.mjs | 2 +- components/peerdom/sources/new-role/new-role.mjs | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/components/peerdom/sources/common/base.mjs b/components/peerdom/sources/common/base.mjs index 32742a8e4cae5..03ee88ac7525e 100644 --- a/components/peerdom/sources/common/base.mjs +++ b/components/peerdom/sources/common/base.mjs @@ -19,14 +19,17 @@ export default { _setLastId(lastId) { this.db.set("lastId", lastId); }, + sortItems(items) { + return items; + }, async emitEvent(maxResults = false) { const lastId = this._getLastId(); const fn = this.getFunction(); - const response = await fn(); + const response = this.sortItems(await fn()); let responseArray = []; - for (const item of response) { + for (const item of response.reverse()) { if (item.id == lastId) break; responseArray.push(item); } diff --git a/components/peerdom/sources/new-member/new-member.mjs b/components/peerdom/sources/new-member/new-member.mjs index ee90181beaa1c..3a5d172f972b8 100644 --- a/components/peerdom/sources/new-member/new-member.mjs +++ b/components/peerdom/sources/new-member/new-member.mjs @@ -15,7 +15,7 @@ export default { return this.peerdom.listPeers; }, getSummary(item) { - return `New Member: ${item.firstName} ${item.lastName}`; + return `New Member: ${item.firstName} ${item.lastName || ""}`; }, }, sampleEmit, diff --git a/components/peerdom/sources/new-role/new-role.mjs b/components/peerdom/sources/new-role/new-role.mjs index 48616c2254f7c..5741b1141cb89 100644 --- a/components/peerdom/sources/new-role/new-role.mjs +++ b/components/peerdom/sources/new-role/new-role.mjs @@ -17,6 +17,9 @@ export default { getSummary(item) { return `New Role Created: ${item.name}`; }, + sortItems(items) { + return items.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt)); + }, }, sampleEmit, };