From 8f8b22589ecad24f80a3ab0560f4b1c42250e29f Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 21 May 2025 15:27:38 -0300 Subject: [PATCH 01/14] ninjaone init --- .../actions/create-ticket/create-ticket.mjs | 54 ++++ .../actions/update-device/update-device.mjs | 33 +++ components/ninjaone/ninjaone.app.mjs | 236 +++++++++++++++++- .../new-device-online-instant.mjs | 96 +++++++ .../new-remote-session-instant.mjs | 107 ++++++++ .../new-ticket-instant/new-ticket-instant.mjs | 79 ++++++ 6 files changed, 601 insertions(+), 4 deletions(-) create mode 100644 components/ninjaone/actions/create-ticket/create-ticket.mjs create mode 100644 components/ninjaone/actions/update-device/update-device.mjs create mode 100644 components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs create mode 100644 components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs create mode 100644 components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs diff --git a/components/ninjaone/actions/create-ticket/create-ticket.mjs b/components/ninjaone/actions/create-ticket/create-ticket.mjs new file mode 100644 index 0000000000000..ebd94bd9d8eef --- /dev/null +++ b/components/ninjaone/actions/create-ticket/create-ticket.mjs @@ -0,0 +1,54 @@ +import ninjaone from "../../ninjaone.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "ninjaone-create-ticket", + name: "Create Support Ticket", + description: "Creates a new support ticket in NinjaOne. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core)", + version: "0.0.{{ts}}", + type: "action", + props: { + ninjaone, + title: { + type: "string", + label: "Title", + description: "Title of the support ticket", + }, + description: { + type: "string", + label: "Description", + description: "Description of the support ticket", + }, + priority: { + propDefinition: [ + ninjaone, + "ticketPriority", + ], + }, + assignedTechnician: { + propDefinition: [ + ninjaone, + "technician", + ], + optional: true, + }, + dueDate: { + type: "string", + label: "Due Date", + description: "Due date for the support ticket. Format: YYYY-MM-DD", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.ninjaone.createSupportTicket({ + title: this.title, + description: this.description, + priority: this.priority, + assignedTechnician: this.assignedTechnician, + dueDate: this.dueDate, + }); + + $.export("$summary", `Ticket created successfully with ID ${response.id}`); + return response; + }, +}; diff --git a/components/ninjaone/actions/update-device/update-device.mjs b/components/ninjaone/actions/update-device/update-device.mjs new file mode 100644 index 0000000000000..adf88be97b94c --- /dev/null +++ b/components/ninjaone/actions/update-device/update-device.mjs @@ -0,0 +1,33 @@ +import ninjaone from "../../ninjaone.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "ninjaone-update-device", + name: "Update Device", + description: "Update details for a specific device in NinjaOne. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core)", + version: "0.0.{{ts}}", + type: "action", + props: { + ninjaone, + deviceId: { + propDefinition: [ + ninjaone, + "deviceId", + ], + }, + deviceAttributes: { + propDefinition: [ + ninjaone, + "deviceAttributes", + ], + type: "string", + label: "Device Attributes", + description: "JSON string of attributes to update on the device", + }, + }, + async run({ $ }) { + const response = await this.ninjaone.updateDevice(this.deviceId, this.deviceAttributes); + $.export("$summary", `Successfully updated device with ID ${this.deviceId}`); + return response; + }, +}; diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index 0945dafedfbfa..585993ced2589 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -1,11 +1,239 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "ninjaone", - propDefinitions: {}, + propDefinitions: { + ticketPriority: { + type: "string", + label: "Ticket Priority", + description: "Filter support tickets by priority", + async options() { + const priorities = await this.getTicketPriorities(); + return priorities.map((priority) => ({ + label: priority.name, + value: priority.id, + })); + }, + }, + ticketStatus: { + type: "string", + label: "Ticket Status", + description: "Filter support tickets by status", + async options() { + const statuses = await this.getTicketStatuses(); + return statuses.map((status) => ({ + label: status.name, + value: status.id, + })); + }, + }, + deviceGroup: { + type: "string", + label: "Device Group", + description: "Specify the device group to monitor", + async options() { + const groups = await this.getDeviceGroups(); + return groups.map((group) => ({ + label: group.name, + value: group.id, + })); + }, + }, + deviceType: { + type: "string", + label: "Device Type", + description: "Specify the device type to monitor", + async options() { + const types = await this.getDeviceTypes(); + return types.map((type) => ({ + label: type.name, + value: type.id, + })); + }, + }, + sessionType: { + type: "string", + label: "Session Type", + description: "Filter remote access sessions by type", + optional: true, + async options() { + const types = await this.getSessionTypes(); + return types.map((type) => ({ + label: type.name, + value: type.id, + })); + }, + }, + technician: { + type: "string", + label: "Technician", + description: "Filter remote access sessions by technician", + optional: true, + async options() { + const technicians = await this.getTechnicians(); + return technicians.map((tech) => ({ + label: tech.name, + value: tech.id, + })); + }, + }, + title: { + type: "string", + label: "Title", + description: "Title of the support ticket", + }, + description: { + type: "string", + label: "Description", + description: "Description of the support ticket", + }, + priority: { + type: "string", + label: "Priority", + description: "Priority level of the support ticket", + }, + assignedTechnician: { + type: "string", + label: "Assigned Technician", + description: "Technician to assign the ticket to", + optional: true, + }, + dueDate: { + type: "string", + label: "Due Date", + description: "Due date for the support ticket", + optional: true, + }, + deviceId: { + type: "string", + label: "Device ID", + description: "Identifier of the device to update", + }, + deviceAttributes: { + type: "string[]", + label: "Device Attributes", + description: "Attributes to update on the device", + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.ninjaone.com/v2"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, method = "GET", path = "/", headers, ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + }); + }, + async getTicketPriorities() { + return this._makeRequest({ + path: "/ticketing/ticket-priorities", + }); + }, + async getTicketStatuses() { + return this._makeRequest({ + path: "/ticketing/ticket-statuses", + }); + }, + async getDeviceGroups() { + return this._makeRequest({ + path: "/device-groups", + }); + }, + async getDeviceTypes() { + return this._makeRequest({ + path: "/device-types", + }); + }, + async getSessionTypes() { + return this._makeRequest({ + path: "/session-types", + }); + }, + async getTechnicians() { + return this._makeRequest({ + path: "/technicians", + }); + }, + async createSupportTicket({ + title, description, priority, assignedTechnician, dueDate, + }) { + return this._makeRequest({ + method: "POST", + path: "/ticketing/ticket", + data: { + title, + description, + priority, + assignedTechnician, + dueDate, + }, + }); + }, + async updateDevice(deviceId, deviceAttributes) { + return this._makeRequest({ + method: "PATCH", + path: `/device/${deviceId}`, + data: JSON.parse(deviceAttributes), + }); + }, + async emitNewSupportTicket({ + ticketPriority, ticketStatus, + }) { + const response = await this._makeRequest({ + path: "/ticketing/ticket", + params: { + priority: ticketPriority, + status: ticketStatus, + }, + }); + response.forEach((ticket) => this.$emit(ticket, { + id: ticket.id, + summary: `New ticket: ${ticket.title}`, + ts: new Date().getTime(), + })); + }, + async emitDeviceOnline({ + deviceGroup, deviceType, + }) { + const response = await this._makeRequest({ + path: "/devices", + params: { + group: deviceGroup, + type: deviceType, + }, + }); + response.forEach((device) => this.$emit(device, { + id: device.id, + summary: `Device online: ${device.name}`, + ts: new Date().getTime(), + })); + }, + async emitRemoteAccessSession({ + sessionType, technician, + }) { + const response = await this._makeRequest({ + path: "/device/{id}/activities", + params: { + sessionType, + technician, + }, + }); + response.forEach((session) => this.$emit(session, { + id: session.id, + summary: `Remote session initiated by ${session.technician}`, + ts: new Date().getTime(), + })); }, }, + version: `0.0.${Date.now()}`, }; diff --git a/components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs b/components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs new file mode 100644 index 0000000000000..64fdff8a36283 --- /dev/null +++ b/components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs @@ -0,0 +1,96 @@ +import ninjaone from "../../ninjaone.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "ninjaone-new-device-online-instant", + name: "New Device Online", + description: "Emit new event when a monitored device comes online. Users can specify a device group or type to monitor. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + ninjaone: { + type: "app", + app: "ninjaone", + }, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + deviceGroup: { + propDefinition: [ + ninjaone, + "deviceGroup", + ], + optional: true, + }, + deviceType: { + propDefinition: [ + ninjaone, + "deviceType", + ], + optional: true, + }, + }, + hooks: { + async deploy() { + const devices = await this.ninjaone.emitDeviceOnline({ + deviceGroup: this.deviceGroup, + deviceType: this.deviceType, + }); + devices.slice(0, 50).forEach((device) => { + this.$emit(device, { + id: device.id, + summary: `Device online: ${device.name}`, + ts: new Date().getTime(), + }); + }); + }, + async activate() { + const webhookData = await this.ninjaone.createWebhook({ + event: "device_online", + targetUrl: this.http.endpoint, + }); + this.db.set("webhookId", webhookData.id); + }, + async deactivate() { + const webhookId = this.db.get("webhookId"); + if (webhookId) { + await this.ninjaone.deleteWebhook(webhookId); + this.db.set("webhookId", null); + } + }, + }, + methods: { + async verifySignature(event) { + const signature = event.headers["x-ninja-signature"]; + const rawBody = event.bodyRaw; + const secretKey = this.ninjaone.$auth.webhook_secret_key; + const computedSignature = crypto.createHmac("sha256", secretKey).update(rawBody) + .digest("base64"); + return computedSignature === signature; + }, + }, + async run(event) { + if (!(await this.verifySignature(event))) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + this.http.respond({ + status: 200, + body: "OK", + }); + + this.$emit(event.body, { + id: event.body.id, + summary: `Device online: ${event.body.name}`, + ts: Date.parse(event.body.timestamp), + }); + }, +}; diff --git a/components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs b/components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs new file mode 100644 index 0000000000000..9efa1be4daeb0 --- /dev/null +++ b/components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs @@ -0,0 +1,107 @@ +import appName from "../../ninjaone.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "ninjaone-new-remote-session-instant", + name: "New Remote Session Instant", + description: "Emit a new event when a remote access session is initiated. Users can filter by session type or technician. [See the documentation](https://resources.ninjarmm.com/api/ninja+rmm+public+api+v2.0.5+webhooks.pdf)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + ninjaone: { + type: "app", + app: "ninjaone", + }, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + sessionType: { + propDefinition: [ + appName, + "sessionType", + ], + }, + technician: { + propDefinition: [ + appName, + "technician", + ], + }, + }, + methods: { + _getWebhookId() { + return this.db.get("webhookId"); + }, + + _setWebhookId(id) { + this.db.set("webhookId", id); + }, + }, + hooks: { + async deploy() { + const sessions = await this.ninjaone.emitRemoteAccessSession({ + sessionType: this.sessionType, + technician: this.technician, + }); + sessions.slice(-50).forEach((session) => { + this.$emit(session, { + id: session.id, + summary: `Remote session initiated by ${session.technician}`, + ts: new Date(session.timestamp).getTime(), + }); + }); + }, + async activate() { + const webhookId = await this.ninjaone._makeRequest({ + method: "POST", + path: "/webhooks", + data: { + event: "remote_session_initiated", + callback_url: this.http.endpoint, + }, + }); + this._setWebhookId(webhookId); + }, + async deactivate() { + const webhookId = this._getWebhookId(); + if (webhookId) { + await this.ninjaone._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + this.db.set("webhookId", null); + } + }, + }, + async run(event) { + const { + headers, body, + } = event; + + const computedSignature = crypto.createHmac("sha256", this.ninjaone.$auth.oauth_access_token) + .update(JSON.stringify(body)) + .digest("base64"); + + if (headers["x-signature"] !== computedSignature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + this.http.respond({ + status: 200, + body: "OK", + }); + this.$emit(body, { + id: body.id, + summary: `Remote session initiated by ${body.technician}`, + ts: new Date(body.timestamp).getTime(), + }); + }, +}; diff --git a/components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs b/components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs new file mode 100644 index 0000000000000..f298221546c96 --- /dev/null +++ b/components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs @@ -0,0 +1,79 @@ +import ninjaone from "../../ninjaone.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "ninjaone-new-ticket-instant", + name: "New Ticket Instant", + description: "Emit new event when a new support ticket is created in NinjaOne. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + ninjaone, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + ticketPriority: { + propDefinition: [ + ninjaone, + "ticketPriority", + ], + optional: true, + }, + ticketStatus: { + propDefinition: [ + ninjaone, + "ticketStatus", + ], + optional: true, + }, + }, + methods: { + _getLastProcessedTimestamp() { + return this.db.get("lastProcessedTimestamp") || 0; + }, + _setLastProcessedTimestamp(timestamp) { + this.db.set("lastProcessedTimestamp", timestamp); + }, + }, + hooks: { + async deploy() { + const lastProcessedTimestamp = this._getLastProcessedTimestamp(); + const tickets = await this.ninjaone.emitNewSupportTicket({ + ticketPriority: this.ticketPriority, + ticketStatus: this.ticketStatus, + }); + + tickets.slice(-50).forEach((ticket) => { + if (new Date(ticket.created_at).getTime() > lastProcessedTimestamp) { + this.$emit(ticket, { + id: ticket.id, + summary: `New ticket: ${ticket.title}`, + ts: Date.parse(ticket.last_modified), + }); + } + }); + + if (tickets.length > 0) { + const latestTimestamp = Math.max(...tickets.map((ticket) => Date.parse(ticket.last_modified))); + this._setLastProcessedTimestamp(latestTimestamp); + } + }, + async activate() { + // Implement webhook creation logic if supported by the API + }, + async deactivate() { + // Implement webhook deletion logic if supported by the API + }, + }, + async run(event) { + const { body } = event; + this.$emit(body, { + id: body.id, + summary: `New ticket: ${body.title}`, + ts: Date.parse(body.last_modified), + }); + }, +}; From d532f7ff9f69ba59ac9d4944e580c90ce4d684ac Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 23 May 2025 09:50:12 -0300 Subject: [PATCH 02/14] [Components] ninjaone #14845 Sources - New Device Online (Instant) Actions - Create Ticket - Update Device --- .../actions/create-ticket/create-ticket.mjs | 176 ++++++-- .../actions/update-device/update-device.mjs | 57 ++- components/ninjaone/common/constants.mjs | 23 + components/ninjaone/common/utils.mjs | 28 ++ components/ninjaone/ninjaone.app.mjs | 425 +++++++++++------- components/ninjaone/package.json | 5 +- components/ninjaone/sources/common/base.mjs | 64 +++ .../new-device-online-instant.mjs | 96 ---- .../new-device-online/new-device-online.mjs | 27 ++ .../sources/new-device-online/test-event.mjs | 18 + .../new-remote-session-instant.mjs | 107 ----- .../new-ticket-instant/new-ticket-instant.mjs | 79 ---- 12 files changed, 625 insertions(+), 480 deletions(-) create mode 100644 components/ninjaone/common/constants.mjs create mode 100644 components/ninjaone/common/utils.mjs create mode 100644 components/ninjaone/sources/common/base.mjs delete mode 100644 components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs create mode 100644 components/ninjaone/sources/new-device-online/new-device-online.mjs create mode 100644 components/ninjaone/sources/new-device-online/test-event.mjs delete mode 100644 components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs delete mode 100644 components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs diff --git a/components/ninjaone/actions/create-ticket/create-ticket.mjs b/components/ninjaone/actions/create-ticket/create-ticket.mjs index ebd94bd9d8eef..c26cdb4c419b2 100644 --- a/components/ninjaone/actions/create-ticket/create-ticket.mjs +++ b/components/ninjaone/actions/create-ticket/create-ticket.mjs @@ -1,54 +1,180 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { + PRIORITY_OPTIONS, + SEVERITY_OPTIONS, + TYPE_OPTIONS, +} from "../../common/constants.mjs"; +import { + normalCase, + parseObject, +} from "../../common/utils.mjs"; import ninjaone from "../../ninjaone.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "ninjaone-create-ticket", name: "Create Support Ticket", - description: "Creates a new support ticket in NinjaOne. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core)", - version: "0.0.{{ts}}", + description: "Creates a new support ticket in NinjaOne. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core#/ticketing/create)", + version: "0.0.1", type: "action", props: { ninjaone, - title: { + clientId: { + propDefinition: [ + ninjaone, + "clientId", + ], + }, + ticketFormId: { + propDefinition: [ + ninjaone, + "ticketFormId", + ], + }, + organizationId: { + propDefinition: [ + ninjaone, + "organizationId", + ], + optional: true, + }, + locationId: { + propDefinition: [ + ninjaone, + "locationId", + ({ organizationId }) => ({ + organizationId, + }), + ], + optional: true, + }, + nodeId: { + propDefinition: [ + ninjaone, + "deviceId", + ({ organizationId }) => ({ + organizationId, + }), + ], + optional: true, + }, + subject: { type: "string", - label: "Title", - description: "Title of the support ticket", + label: "Subject", + description: "The subject of the ticket", + }, + descriptionPublic: { + type: "boolean", + label: "Public Description", + description: "Whether the ticket's description is public or not", }, - description: { + descriptionBody: { type: "string", - label: "Description", - description: "Description of the support ticket", + label: "Description Body", + description: "The description of the ticket", + optional: true, }, - priority: { + descriptionHTML: { + type: "string", + label: "Description HTML", + description: "The description HTML of the ticket", + optional: true, + }, + descriptiontimeTracked: { + type: "integer", + label: "Time Tracked", + description: "Time in seconds", + optional: true, + }, + descriptionDuplicateInIncidents: { + type: "boolean", + label: "Duplicate In Incidents", + description: "Whether the ticket will duplicate in the same incident", + optional: true, + }, + status: { propDefinition: [ ninjaone, - "ticketPriority", + "status", ], }, - assignedTechnician: { + type: { + type: "string", + label: "Type", + description: "The type of the ticket", + options: TYPE_OPTIONS, + optional: true, + }, + cc: { + type: "string[]", + label: "CC", + description: "A list of emails to be copied in the notification email", + optional: true, + }, + assignedAppUserId: { propDefinition: [ ninjaone, - "technician", + "assignedAppUserId", ], optional: true, }, - dueDate: { + severity: { + type: "string", + label: "Severity", + description: "The severity's level of the ticket", + options: SEVERITY_OPTIONS, + optional: true, + }, + priority: { type: "string", - label: "Due Date", - description: "Due date for the support ticket. Format: YYYY-MM-DD", + label: "Priority", + description: "The priority's level of the ticket", + options: PRIORITY_OPTIONS, + optional: true, + }, + tags: { + propDefinition: [ + ninjaone, + "tags", + ], optional: true, }, }, async run({ $ }) { - const response = await this.ninjaone.createSupportTicket({ - title: this.title, - description: this.description, - priority: this.priority, - assignedTechnician: this.assignedTechnician, - dueDate: this.dueDate, - }); + try { + const { + ninjaone, + descriptionPublic, + descriptionBody, + descriptionHTML, + descriptiontimeTracked, + descriptionDuplicateInIncidents, + cc, + tags, + ...data + } = this; + + const response = await ninjaone.createSupportTicket({ + $, + data: { + ...data, + description: { + public: descriptionPublic, + body: descriptionBody, + htmlBody: descriptionHTML, + timeTracked: descriptiontimeTracked, + duplicateInIncidents: descriptionDuplicateInIncidents, + }, + cc: { + emails: parseObject(cc), + }, + tags: parseObject(tags), + }, + }); - $.export("$summary", `Ticket created successfully with ID ${response.id}`); - return response; + $.export("$summary", `Ticket created successfully with ID: ${response.id}`); + return response; + } catch ({ response }) { + throw new ConfigurationError(normalCase(response.data.resultCode) || response.data); + } }, }; diff --git a/components/ninjaone/actions/update-device/update-device.mjs b/components/ninjaone/actions/update-device/update-device.mjs index adf88be97b94c..b441b8667cb38 100644 --- a/components/ninjaone/actions/update-device/update-device.mjs +++ b/components/ninjaone/actions/update-device/update-device.mjs @@ -1,11 +1,10 @@ import ninjaone from "../../ninjaone.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "ninjaone-update-device", name: "Update Device", description: "Update details for a specific device in NinjaOne. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { ninjaone, @@ -14,20 +13,60 @@ export default { ninjaone, "deviceId", ], + description: "The Id of the device to update ", }, - deviceAttributes: { + displayName: { + type: "string", + label: "Display Name", + description: "The name of the device", + optional: true, + }, + nodeRoleId: { propDefinition: [ ninjaone, - "deviceAttributes", + "nodeRoleId", ], - type: "string", - label: "Device Attributes", - description: "JSON string of attributes to update on the device", + optional: true, + }, + policyId: { + propDefinition: [ + ninjaone, + "policyId", + ], + optional: true, + }, + organizationId: { + propDefinition: [ + ninjaone, + "organizationId", + ], + optional: true, + }, + locationId: { + propDefinition: [ + ninjaone, + "locationId", + ({ organizationId }) => ({ + organizationId, + }), + ], + optional: true, }, }, async run({ $ }) { - const response = await this.ninjaone.updateDevice(this.deviceId, this.deviceAttributes); - $.export("$summary", `Successfully updated device with ID ${this.deviceId}`); + const { + ninjaone, + deviceId, + ...data + } = this; + + const response = await ninjaone.updateDevice({ + $, + deviceId, + data, + }); + + $.export("$summary", `Successfully updated device with ID ${deviceId}`); return response; }, }; diff --git a/components/ninjaone/common/constants.mjs b/components/ninjaone/common/constants.mjs new file mode 100644 index 0000000000000..3f2ba884706a7 --- /dev/null +++ b/components/ninjaone/common/constants.mjs @@ -0,0 +1,23 @@ +export const LIMIT = 100; + +export const TYPE_OPTIONS = [ + "PROBLEM", + "QUESTION", + "INCIDENT", + "TASK", +]; + +export const SEVERITY_OPTIONS = [ + "NONE", + "MINOR", + "MODERATE", + "MAJOR", + "CRITICAL", +]; + +export const PRIORITY_OPTIONS = [ + "NONE", + "LOW", + "MEDIUM", + "HIGH", +]; diff --git a/components/ninjaone/common/utils.mjs b/components/ninjaone/common/utils.mjs new file mode 100644 index 0000000000000..59f5a843b38ac --- /dev/null +++ b/components/ninjaone/common/utils.mjs @@ -0,0 +1,28 @@ +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; +}; + +export const normalCase = (s) => + s?.replace (/^[-_]*(.)/, (_, c) => c.toUpperCase()) + .replace (/[-_]+(.)/g, (_, c) => " " + c); diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index 585993ced2589..fbd7050fcf8ec 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -1,239 +1,338 @@ import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/constants.mjs"; export default { type: "app", app: "ninjaone", propDefinitions: { - ticketPriority: { + clientId: { type: "string", - label: "Ticket Priority", - description: "Filter support tickets by priority", - async options() { - const priorities = await this.getTicketPriorities(); - return priorities.map((priority) => ({ - label: priority.name, - value: priority.id, - })); + label: "Client Id", + description: "The Id of the client related to the ticket", + async options({ prevContext }) { + const data = await this.listOrganizations({ + params: { + pageSize: LIMIT, + after: prevContext.lastId, + }, + }); + + return { + options: data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })), + context: { + lastId: data[data.length - 1]?.id, + }, + }; }, }, - ticketStatus: { + ticketFormId: { type: "string", - label: "Ticket Status", - description: "Filter support tickets by status", + label: "Ticket Form ID", + description: "The ID of the ticket form to the ticket", async options() { - const statuses = await this.getTicketStatuses(); - return statuses.map((status) => ({ - label: status.name, - value: status.id, + const data = await this.listTicketForms(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, })); }, }, - deviceGroup: { + locationId: { type: "string", - label: "Device Group", - description: "Specify the device group to monitor", - async options() { - const groups = await this.getDeviceGroups(); - return groups.map((group) => ({ - label: group.name, - value: group.id, - })); + label: "Location ID", + description: "The ID of the location to the ticket", + async options({ + prevContext, organizationId, + }) { + const data = await this.listLocations({ + organizationId, + params: { + pageSize: LIMIT, + after: prevContext.lastId, + }, + }); + + return { + options: data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })), + context: { + lastId: data[data.length - 1]?.id, + }, + }; }, }, - deviceType: { + deviceId: { type: "string", - label: "Device Type", - description: "Specify the device type to monitor", - async options() { - const types = await this.getDeviceTypes(); - return types.map((type) => ({ - label: type.name, - value: type.id, - })); + label: "Device ID", + description: "The Id of the device to the ticket", + async options({ + prevContext, organizationId, + }) { + const data = await this.listDevices({ + organizationId, + params: { + pageSize: LIMIT, + after: prevContext.lastId, + }, + }); + + return { + options: data.map(({ + id: value, systemName: label, + }) => ({ + label, + value, + })), + context: { + lastId: data[data.length - 1]?.id, + }, + }; }, }, - sessionType: { + tags: { + type: "string[]", + label: "Tags", + description: "A list of tags related to ticket.", + async options({ prevContext }) { + const { tags } = await this.listTags({ + params: { + pageSize: LIMIT, + after: prevContext.lastId, + }, + }); + + return tags.map(({ name }) => name); + }, + }, + status: { type: "string", - label: "Session Type", - description: "Filter remote access sessions by type", - optional: true, + label: "Status", + description: "The status of the ticket", async options() { - const types = await this.getSessionTypes(); - return types.map((type) => ({ - label: type.name, - value: type.id, + const data = await this.listStatuses(); + + return data.map(({ + statusId: value, displayName: label, + }) => ({ + label, + value, })); }, }, - technician: { + assignedAppUserId: { type: "string", - label: "Technician", - description: "Filter remote access sessions by technician", - optional: true, + label: "Assigned App User ID", + description: "User ID that will be assigned to the ticket", async options() { - const technicians = await this.getTechnicians(); - return technicians.map((tech) => ({ - label: tech.name, - value: tech.id, + const data = await this.listUsers({ + params: { + userType: "TECHNICIAN", + }, + }); + + return data.map(({ + id: value, firstName, lastName, email, + }) => ({ + label: `${firstName} ${lastName} - ${email}`, + value, })); }, }, - title: { + nodeRoleId: { type: "string", - label: "Title", - description: "Title of the support ticket", - }, - description: { - type: "string", - label: "Description", - description: "Description of the support ticket", - }, - priority: { - type: "string", - label: "Priority", - description: "Priority level of the support ticket", - }, - assignedTechnician: { - type: "string", - label: "Assigned Technician", - description: "Technician to assign the ticket to", - optional: true, + label: "Node Role Id", + description: "The id of the device role", + async options() { + const data = await this.listRoles(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, }, - dueDate: { + policyId: { type: "string", - label: "Due Date", - description: "Due date for the support ticket", - optional: true, + label: "Policy Id", + description: "The id of the policiy override", + async options() { + const data = await this.listPolicies(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, }, - deviceId: { + organizationId: { type: "string", - label: "Device ID", - description: "Identifier of the device to update", - }, - deviceAttributes: { - type: "string[]", - label: "Device Attributes", - description: "Attributes to update on the device", + label: "Organization Id", + description: "The id of the organization", + async options({ prevContext }) { + const data = await this.listOrganizations({ + params: { + pageSize: LIMIT, + after: prevContext.lastId, + }, + }); + + return { + options: data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })), + context: { + lastId: data[data.length - 1]?.id, + }, + }; + }, }, }, methods: { _baseUrl() { return "https://api.ninjaone.com/v2"; }, - async _makeRequest(opts = {}) { - const { - $ = this, method = "GET", path = "/", headers, ...otherOpts - } = opts; + _headers() { + return { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { return axios($, { - ...otherOpts, - method, url: this._baseUrl() + path, - headers: { - ...headers, - Authorization: `Bearer ${this.$auth.oauth_access_token}`, - }, + headers: this._headers(), + ...opts, }); }, - async getTicketPriorities() { + createSupportTicket(opts = {}) { return this._makeRequest({ - path: "/ticketing/ticket-priorities", + method: "POST", + path: "/ticketing/ticket", + ...opts, }); }, - async getTicketStatuses() { + listOrganizations(opts = {}) { return this._makeRequest({ - path: "/ticketing/ticket-statuses", + path: "/organizations", + ...opts, }); }, - async getDeviceGroups() { + listTicketForms(opts = {}) { return this._makeRequest({ - path: "/device-groups", + path: "/ticketing/ticket-form", + ...opts, }); }, - async getDeviceTypes() { + listLocations({ + organizationId, ...opts + }) { return this._makeRequest({ - path: "/device-types", + path: `organization/${organizationId}/locations`, + ...opts, }); }, - async getSessionTypes() { + listDevices({ + organizationId, ...opts + }) { return this._makeRequest({ - path: "/session-types", + path: `organization/${organizationId}/devices`, + ...opts, }); }, - async getTechnicians() { + listTags(opts = {}) { return this._makeRequest({ - path: "/technicians", + path: "/tag", + ...opts, }); }, - async createSupportTicket({ - title, description, priority, assignedTechnician, dueDate, - }) { + listStatuses(opts = {}) { return this._makeRequest({ - method: "POST", - path: "/ticketing/ticket", - data: { - title, - description, - priority, - assignedTechnician, - dueDate, - }, + path: "/ticketing/statuses", + ...opts, }); }, - async updateDevice(deviceId, deviceAttributes) { + listUsers(opts = {}) { return this._makeRequest({ - method: "PATCH", - path: `/device/${deviceId}`, - data: JSON.parse(deviceAttributes), + path: "/users", + ...opts, }); }, - async emitNewSupportTicket({ - ticketPriority, ticketStatus, - }) { - const response = await this._makeRequest({ - path: "/ticketing/ticket", - params: { - priority: ticketPriority, - status: ticketStatus, - }, + listRoles(opts = {}) { + return this._makeRequest({ + path: "/roles", + ...opts, }); - response.forEach((ticket) => this.$emit(ticket, { - id: ticket.id, - summary: `New ticket: ${ticket.title}`, - ts: new Date().getTime(), - })); - }, - async emitDeviceOnline({ - deviceGroup, deviceType, - }) { - const response = await this._makeRequest({ - path: "/devices", - params: { - group: deviceGroup, - type: deviceType, - }, + }, + listPolicies(opts = {}) { + return this._makeRequest({ + path: "/policies", + ...opts, }); - response.forEach((device) => this.$emit(device, { - id: device.id, - summary: `Device online: ${device.name}`, - ts: new Date().getTime(), - })); - }, - async emitRemoteAccessSession({ - sessionType, technician, + }, + listActivities(opts = {}) { + return this._makeRequest({ + path: "/activities", + ...opts, + }); + }, + updateDevice({ + deviceId, ...opts }) { - const response = await this._makeRequest({ - path: "/device/{id}/activities", - params: { - sessionType, - technician, - }, + return this._makeRequest({ + method: "PATCH", + path: `/device/${deviceId}`, + ...opts, }); - response.forEach((session) => this.$emit(session, { - id: session.id, - summary: `Remote session initiated by ${session.technician}`, - ts: new Date().getTime(), - })); + }, + async *paginate({ + fn, params = {}, maxResults = null, ...opts + }) { + let hasMore = false; + let count = 0; + let lastId = 0; + const newerThan = params.newerThan; + + do { + if (lastId) { + params.olderThan = lastId; + if (params.newerThan) delete params.newerThan; + } + + const { activities } = await fn({ + params, + ...opts, + }); + for (const d of activities) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = activities.length && (lastId && (lastId > newerThan)); + + } while (hasMore); }, }, - version: `0.0.${Date.now()}`, }; diff --git a/components/ninjaone/package.json b/components/ninjaone/package.json index 968834403f082..8115cd7a84d3d 100644 --- a/components/ninjaone/package.json +++ b/components/ninjaone/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/ninjaone", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream NinjaOne Components", "main": "ninjaone.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/ninjaone/sources/common/base.mjs b/components/ninjaone/sources/common/base.mjs new file mode 100644 index 0000000000000..1a7ee2318dc4a --- /dev/null +++ b/components/ninjaone/sources/common/base.mjs @@ -0,0 +1,64 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import ninjaone from "../../ninjaone.app.mjs"; + +export default { + props: { + ninjaone, + 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); + }, + async emitEvent(maxResults = false) { + const lastId = this._getLastId(); + + const response = this.ninjaone.paginate({ + fn: this.ninjaone.listActivities, + params: { + newerThan: lastId, + ...this.getParams(), + }, + }); + + let responseArray = []; + for await (const item of response) { + responseArray.push(item); + } + + responseArray = this.filterEvents(responseArray); + + 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.parse(item.activityTime || new Date()), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs b/components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs deleted file mode 100644 index 64fdff8a36283..0000000000000 --- a/components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs +++ /dev/null @@ -1,96 +0,0 @@ -import ninjaone from "../../ninjaone.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; - -export default { - key: "ninjaone-new-device-online-instant", - name: "New Device Online", - description: "Emit new event when a monitored device comes online. Users can specify a device group or type to monitor. [See the documentation]()", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - ninjaone: { - type: "app", - app: "ninjaone", - }, - http: { - type: "$.interface.http", - customResponse: true, - }, - db: "$.service.db", - deviceGroup: { - propDefinition: [ - ninjaone, - "deviceGroup", - ], - optional: true, - }, - deviceType: { - propDefinition: [ - ninjaone, - "deviceType", - ], - optional: true, - }, - }, - hooks: { - async deploy() { - const devices = await this.ninjaone.emitDeviceOnline({ - deviceGroup: this.deviceGroup, - deviceType: this.deviceType, - }); - devices.slice(0, 50).forEach((device) => { - this.$emit(device, { - id: device.id, - summary: `Device online: ${device.name}`, - ts: new Date().getTime(), - }); - }); - }, - async activate() { - const webhookData = await this.ninjaone.createWebhook({ - event: "device_online", - targetUrl: this.http.endpoint, - }); - this.db.set("webhookId", webhookData.id); - }, - async deactivate() { - const webhookId = this.db.get("webhookId"); - if (webhookId) { - await this.ninjaone.deleteWebhook(webhookId); - this.db.set("webhookId", null); - } - }, - }, - methods: { - async verifySignature(event) { - const signature = event.headers["x-ninja-signature"]; - const rawBody = event.bodyRaw; - const secretKey = this.ninjaone.$auth.webhook_secret_key; - const computedSignature = crypto.createHmac("sha256", secretKey).update(rawBody) - .digest("base64"); - return computedSignature === signature; - }, - }, - async run(event) { - if (!(await this.verifySignature(event))) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - this.http.respond({ - status: 200, - body: "OK", - }); - - this.$emit(event.body, { - id: event.body.id, - summary: `Device online: ${event.body.name}`, - ts: Date.parse(event.body.timestamp), - }); - }, -}; diff --git a/components/ninjaone/sources/new-device-online/new-device-online.mjs b/components/ninjaone/sources/new-device-online/new-device-online.mjs new file mode 100644 index 0000000000000..7742bf392040a --- /dev/null +++ b/components/ninjaone/sources/new-device-online/new-device-online.mjs @@ -0,0 +1,27 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "ninjaone-new-device-online", + name: "New Device Online", + description: "Emit new event when a monitored device comes online. Users can specify a device group or type to monitor.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getParams() { + return { + class: "DEVICE", + }; + }, + filterEvents(events) { + return events.filter((item) => item.statusCode === "SYSTEM_REBOOTED"); + }, + getSummary(item) { + return `Device online: ${item.deviceId}`; + }, + }, + sampleEmit, +}; diff --git a/components/ninjaone/sources/new-device-online/test-event.mjs b/components/ninjaone/sources/new-device-online/test-event.mjs new file mode 100644 index 0000000000000..a2e807d01d927 --- /dev/null +++ b/components/ninjaone/sources/new-device-online/test-event.mjs @@ -0,0 +1,18 @@ +export default { + "id": 26, + "activityTime": 1747946704, + "deviceId": 1, + "activityType": "MONITOR", + "statusCode": "SYSTEM_REBOOTED", + "status": "Rebooted", + "message": "System rebooted at '2025-05-22T20:43:26Z'", + "type": "Monitor", + "data": { + "message": { + "code": "agent_act_sys_reboot", + "params": { + "reboot_date": "2025-05-22T20:43:26Z" + } + } + } +} \ No newline at end of file diff --git a/components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs b/components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs deleted file mode 100644 index 9efa1be4daeb0..0000000000000 --- a/components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs +++ /dev/null @@ -1,107 +0,0 @@ -import appName from "../../ninjaone.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; - -export default { - key: "ninjaone-new-remote-session-instant", - name: "New Remote Session Instant", - description: "Emit a new event when a remote access session is initiated. Users can filter by session type or technician. [See the documentation](https://resources.ninjarmm.com/api/ninja+rmm+public+api+v2.0.5+webhooks.pdf)", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - ninjaone: { - type: "app", - app: "ninjaone", - }, - http: { - type: "$.interface.http", - customResponse: true, - }, - db: "$.service.db", - sessionType: { - propDefinition: [ - appName, - "sessionType", - ], - }, - technician: { - propDefinition: [ - appName, - "technician", - ], - }, - }, - methods: { - _getWebhookId() { - return this.db.get("webhookId"); - }, - - _setWebhookId(id) { - this.db.set("webhookId", id); - }, - }, - hooks: { - async deploy() { - const sessions = await this.ninjaone.emitRemoteAccessSession({ - sessionType: this.sessionType, - technician: this.technician, - }); - sessions.slice(-50).forEach((session) => { - this.$emit(session, { - id: session.id, - summary: `Remote session initiated by ${session.technician}`, - ts: new Date(session.timestamp).getTime(), - }); - }); - }, - async activate() { - const webhookId = await this.ninjaone._makeRequest({ - method: "POST", - path: "/webhooks", - data: { - event: "remote_session_initiated", - callback_url: this.http.endpoint, - }, - }); - this._setWebhookId(webhookId); - }, - async deactivate() { - const webhookId = this._getWebhookId(); - if (webhookId) { - await this.ninjaone._makeRequest({ - method: "DELETE", - path: `/webhooks/${webhookId}`, - }); - this.db.set("webhookId", null); - } - }, - }, - async run(event) { - const { - headers, body, - } = event; - - const computedSignature = crypto.createHmac("sha256", this.ninjaone.$auth.oauth_access_token) - .update(JSON.stringify(body)) - .digest("base64"); - - if (headers["x-signature"] !== computedSignature) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - this.http.respond({ - status: 200, - body: "OK", - }); - this.$emit(body, { - id: body.id, - summary: `Remote session initiated by ${body.technician}`, - ts: new Date(body.timestamp).getTime(), - }); - }, -}; diff --git a/components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs b/components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs deleted file mode 100644 index f298221546c96..0000000000000 --- a/components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs +++ /dev/null @@ -1,79 +0,0 @@ -import ninjaone from "../../ninjaone.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "ninjaone-new-ticket-instant", - name: "New Ticket Instant", - description: "Emit new event when a new support ticket is created in NinjaOne. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core)", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - ninjaone, - http: { - type: "$.interface.http", - customResponse: false, - }, - db: "$.service.db", - ticketPriority: { - propDefinition: [ - ninjaone, - "ticketPriority", - ], - optional: true, - }, - ticketStatus: { - propDefinition: [ - ninjaone, - "ticketStatus", - ], - optional: true, - }, - }, - methods: { - _getLastProcessedTimestamp() { - return this.db.get("lastProcessedTimestamp") || 0; - }, - _setLastProcessedTimestamp(timestamp) { - this.db.set("lastProcessedTimestamp", timestamp); - }, - }, - hooks: { - async deploy() { - const lastProcessedTimestamp = this._getLastProcessedTimestamp(); - const tickets = await this.ninjaone.emitNewSupportTicket({ - ticketPriority: this.ticketPriority, - ticketStatus: this.ticketStatus, - }); - - tickets.slice(-50).forEach((ticket) => { - if (new Date(ticket.created_at).getTime() > lastProcessedTimestamp) { - this.$emit(ticket, { - id: ticket.id, - summary: `New ticket: ${ticket.title}`, - ts: Date.parse(ticket.last_modified), - }); - } - }); - - if (tickets.length > 0) { - const latestTimestamp = Math.max(...tickets.map((ticket) => Date.parse(ticket.last_modified))); - this._setLastProcessedTimestamp(latestTimestamp); - } - }, - async activate() { - // Implement webhook creation logic if supported by the API - }, - async deactivate() { - // Implement webhook deletion logic if supported by the API - }, - }, - async run(event) { - const { body } = event; - this.$emit(body, { - id: body.id, - summary: `New ticket: ${body.title}`, - ts: Date.parse(body.last_modified), - }); - }, -}; From 871df825e4143f36f97d6fb0b99228e2f0430a32 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 23 May 2025 09:56:15 -0300 Subject: [PATCH 03/14] pnpm update --- pnpm-lock.yaml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd16d7649229d..c74b5ec6dfaab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6041,8 +6041,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/heyreach: - specifiers: {} + components/heyreach: {} components/heysummit: dependencies: @@ -8567,8 +8566,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/neetoform: - specifiers: {} + components/neetoform: {} components/neetoinvoice: dependencies: @@ -8763,7 +8761,11 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/ninjaone: {} + components/ninjaone: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/ninox: dependencies: @@ -15602,7 +15604,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))(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)(tsx@4.19.4)(typescript@5.7.2)(yaml@2.6.1) @@ -35833,6 +35835,8 @@ 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: @@ -49495,7 +49499,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))(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 @@ -49513,6 +49517,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 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): dependencies: From e85aacae16b36d8d28ebf6ecd12f7bcd812ea045 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 23 May 2025 09:57:58 -0300 Subject: [PATCH 04/14] pnpm update --- pnpm-lock.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c74b5ec6dfaab..dc6c3a067fe35 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15604,7 +15604,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@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(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)(tsx@4.19.4)(typescript@5.7.2)(yaml@2.6.1) @@ -49499,7 +49499,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@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(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 @@ -49517,7 +49517,6 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 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): dependencies: From 1517d45eeb0b4b13d2309069d7775c9189c36b38 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 23 May 2025 10:04:35 -0300 Subject: [PATCH 05/14] pnpm update --- pnpm-lock.yaml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1e2173a85967..9cfc2b68be450 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6045,8 +6045,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/heylibby: - specifiers: {} + components/heylibby: {} components/heyreach: {} @@ -8573,11 +8572,9 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/neetocal: - specifiers: {} + components/neetocal: {} - components/neetodesk: - specifiers: {} + components/neetodesk: {} components/neetoform: {} @@ -15485,14 +15482,6 @@ importers: specifier: ^6.0.0 version: 6.2.0 - modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/cjs: {} - - modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/esm: {} - - modelcontextprotocol/node_modules2/zod-to-json-schema/dist/cjs: {} - - modelcontextprotocol/node_modules2/zod-to-json-schema/dist/esm: {} - packages/ai: dependencies: '@pipedream/sdk': From 3ceb95a282373fbb1a2b1d12be6599d842fb1347 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 12:49:51 -0300 Subject: [PATCH 06/14] Update components/ninjaone/ninjaone.app.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- components/ninjaone/ninjaone.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index fbd7050fcf8ec..9db65ab042d09 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -168,7 +168,7 @@ export default { policyId: { type: "string", label: "Policy Id", - description: "The id of the policiy override", + description: "The id of the policy override", async options() { const data = await this.listPolicies(); From 8eeca802ae5b056b743afa8e857a6b602b730980 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 12:51:52 -0300 Subject: [PATCH 07/14] some adjusts --- components/ninjaone/ninjaone.app.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index 9db65ab042d09..13a95f5cc07c8 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -247,7 +247,7 @@ export default { organizationId, ...opts }) { return this._makeRequest({ - path: `organization/${organizationId}/locations`, + path: `/organization/${organizationId}/locations`, ...opts, }); }, @@ -255,7 +255,7 @@ export default { organizationId, ...opts }) { return this._makeRequest({ - path: `organization/${organizationId}/devices`, + path: `/organization/${organizationId}/devices`, ...opts, }); }, From 07b7001e8f74688103f0ac9f54eee65abb16ee09 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 17:59:29 -0300 Subject: [PATCH 08/14] Update components/ninjaone/sources/new-device-online/new-device-online.mjs Co-authored-by: michelle0927 --- .../ninjaone/sources/new-device-online/new-device-online.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ninjaone/sources/new-device-online/new-device-online.mjs b/components/ninjaone/sources/new-device-online/new-device-online.mjs index 7742bf392040a..4c0f321b3a014 100644 --- a/components/ninjaone/sources/new-device-online/new-device-online.mjs +++ b/components/ninjaone/sources/new-device-online/new-device-online.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "ninjaone-new-device-online", name: "New Device Online", - description: "Emit new event when a monitored device comes online. Users can specify a device group or type to monitor.", + description: "Emit new event when a monitored device comes online.", version: "0.0.1", type: "source", dedupe: "unique", From 68a4a8ee5b72943a9261acd51bf8c68894cf4025 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 17:59:44 -0300 Subject: [PATCH 09/14] Update components/ninjaone/ninjaone.app.mjs Co-authored-by: michelle0927 --- components/ninjaone/ninjaone.app.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index 13a95f5cc07c8..15abd63639da9 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -182,8 +182,8 @@ export default { }, organizationId: { type: "string", - label: "Organization Id", - description: "The id of the organization", + label: "Organization ID", + description: "The ID of the organization", async options({ prevContext }) { const data = await this.listOrganizations({ params: { From d4a2592a43fac4967065babb106736126c62b54a Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 17:59:54 -0300 Subject: [PATCH 10/14] Update components/ninjaone/ninjaone.app.mjs Co-authored-by: michelle0927 --- components/ninjaone/ninjaone.app.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index 15abd63639da9..8f3544594c820 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -167,8 +167,8 @@ export default { }, policyId: { type: "string", - label: "Policy Id", - description: "The id of the policy override", + label: "Policy ID", + description: "The ID of the policy override", async options() { const data = await this.listPolicies(); From 3c345b2193664cde862338116b7b92ce1fd678df Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 18:00:07 -0300 Subject: [PATCH 11/14] Update components/ninjaone/ninjaone.app.mjs Co-authored-by: michelle0927 --- components/ninjaone/ninjaone.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index 8f3544594c820..958ffa87ac902 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -153,7 +153,7 @@ export default { nodeRoleId: { type: "string", label: "Node Role Id", - description: "The id of the device role", + description: "The ID of the device role", async options() { const data = await this.listRoles(); From 12e1d48f0a4180195709775cef23c2fc63f3d01b Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 18:02:18 -0300 Subject: [PATCH 12/14] Update components/ninjaone/ninjaone.app.mjs Co-authored-by: michelle0927 --- components/ninjaone/ninjaone.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index 958ffa87ac902..2724c8568d247 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -76,7 +76,7 @@ export default { deviceId: { type: "string", label: "Device ID", - description: "The Id of the device to the ticket", + description: "The ID of the device to the ticket", async options({ prevContext, organizationId, }) { From 9d078b906eba9b9bc32ba4f7f0734728c89b9c2b Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 18:07:33 -0300 Subject: [PATCH 13/14] Update components/ninjaone/ninjaone.app.mjs Co-authored-by: michelle0927 --- components/ninjaone/ninjaone.app.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ninjaone/ninjaone.app.mjs b/components/ninjaone/ninjaone.app.mjs index 2724c8568d247..7a66f9eccf78f 100644 --- a/components/ninjaone/ninjaone.app.mjs +++ b/components/ninjaone/ninjaone.app.mjs @@ -7,8 +7,8 @@ export default { propDefinitions: { clientId: { type: "string", - label: "Client Id", - description: "The Id of the client related to the ticket", + label: "Client ID", + description: "The ID of the client related to the ticket", async options({ prevContext }) { const data = await this.listOrganizations({ params: { From 1a8c0caa1c2d8ea8f658655589df7b0c28448433 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 26 May 2025 18:07:46 -0300 Subject: [PATCH 14/14] Update components/ninjaone/actions/update-device/update-device.mjs Co-authored-by: michelle0927 --- components/ninjaone/actions/update-device/update-device.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ninjaone/actions/update-device/update-device.mjs b/components/ninjaone/actions/update-device/update-device.mjs index b441b8667cb38..f9e242e64cf11 100644 --- a/components/ninjaone/actions/update-device/update-device.mjs +++ b/components/ninjaone/actions/update-device/update-device.mjs @@ -13,7 +13,7 @@ export default { ninjaone, "deviceId", ], - description: "The Id of the device to update ", + description: "The ID of the device to update ", }, displayName: { type: "string",