- 
                Notifications
    You must be signed in to change notification settings 
- Fork 5.5k
[Components] ninjaone - WIP #14892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Components] ninjaone - WIP #14892
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| import app from "../../ninjaone.app.mjs"; | ||
|  | ||
| export default { | ||
| key: "ninjaone-create-ticket", | ||
| name: "Create Ticket", | ||
| description: "Create 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: { | ||
| app, | ||
| clientId: { | ||
| label: "Client ID", | ||
| description: "The identifier of the client. Organization identifier.", | ||
| propDefinition: [ | ||
| app, | ||
| "organizationId", | ||
| ], | ||
| }, | ||
| ticketFormId: { | ||
| propDefinition: [ | ||
| app, | ||
| "ticketFormId", | ||
| ], | ||
| }, | ||
| subject: { | ||
| type: "string", | ||
| label: "Subject", | ||
| description: "The subject of the ticket. Eg. `CPU with problems`.", | ||
| }, | ||
| status: { | ||
| propDefinition: [ | ||
| app, | ||
| "ticketStatus", | ||
| ], | ||
| }, | ||
| type: { | ||
| type: "string", | ||
| label: "Ticket Type", | ||
| description: "The type of the ticket.", | ||
| optional: true, | ||
| options: [ | ||
| "PROBLEM", | ||
| "QUESTION", | ||
| "INCIDENT", | ||
| "TASK", | ||
| ], | ||
| }, | ||
| assignedAppUserId: { | ||
| propDefinition: [ | ||
| app, | ||
| "assignedAppUserId", | ||
| ], | ||
| }, | ||
| description: { | ||
| type: "string", | ||
| label: "Description", | ||
| description: "The description of the support ticket", | ||
| optional: false, | ||
| }, | ||
| isDescriptionPublic: { | ||
| type: "boolean", | ||
| label: "Description Public", | ||
| description: "Whether the description of the ticket is public.", | ||
| optional: true, | ||
| }, | ||
| parentTicketId: { | ||
| type: "string", | ||
| label: "Parent Ticket ID", | ||
| description: "The identifier of the parent ticket.", | ||
| optional: true, | ||
| }, | ||
| priority: { | ||
| type: "string", | ||
| label: "Ticket Priority", | ||
| description: "The priority of the ticket", | ||
| optional: true, | ||
| options: [ | ||
| "NONE", | ||
| "LOW", | ||
| "MEDIUM", | ||
| "HIGH", | ||
| ], | ||
| }, | ||
| }, | ||
| methods: { | ||
| createTicket(args = {}) { | ||
| return this.app.post({ | ||
| path: "/ticketing/ticket", | ||
| ...args, | ||
| }); | ||
| }, | ||
| }, | ||
| async run({ $ }) { | ||
| const { | ||
| createTicket, | ||
| clientId, | ||
| ticketFormId, | ||
| subject, | ||
| status, | ||
| type, | ||
| assignedAppUserId, | ||
| description, | ||
| // isDescriptionPublic, | ||
| parentTicketId, | ||
| priority, | ||
| } = this; | ||
|  | ||
| const response = await createTicket({ | ||
| $, | ||
| data: { | ||
| clientId, | ||
| ticketFormId, | ||
| subject, | ||
| status, | ||
| type, | ||
| assignedAppUserId, | ||
| description, | ||
| // isDescriptionPublic, | ||
| parentTicketId, | ||
| priority, | ||
| }, | ||
| }); | ||
|  | ||
|         
                  GTFalcao marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| $.export("$summary", "Successfully created ticket."); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have the ticket's ID in the response to include in the summary? | ||
|  | ||
| return response; | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,50 @@ | ||||||||||||||||||||||||||||||||
| import app from "../../ninjaone.app.mjs"; | ||||||||||||||||||||||||||||||||
| import utils from "../../common/utils.mjs"; | ||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||
| export default { | ||||||||||||||||||||||||||||||||
| key: "ninjaone-update-device-custom-fields", | ||||||||||||||||||||||||||||||||
| name: "Update Device Custom Fields", | ||||||||||||||||||||||||||||||||
| description: "Update custom fields for a device in NinjaOne. Requires a device identifier. Optionally update the device name, group, or status. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core#/devices/updateNodeAttributeValues).", | ||||||||||||||||||||||||||||||||
| version: "0.0.1", | ||||||||||||||||||||||||||||||||
| type: "action", | ||||||||||||||||||||||||||||||||
| props: { | ||||||||||||||||||||||||||||||||
| app, | ||||||||||||||||||||||||||||||||
| deviceId: { | ||||||||||||||||||||||||||||||||
| propDefinition: [ | ||||||||||||||||||||||||||||||||
| app, | ||||||||||||||||||||||||||||||||
| "deviceId", | ||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| data: { | ||||||||||||||||||||||||||||||||
| type: "object", | ||||||||||||||||||||||||||||||||
| label: "Custom Fields", | ||||||||||||||||||||||||||||||||
| description: "Additional custom fields to update.", | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| 
      Comment on lines
    
      +18
     to 
      +23
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add validation and examples for custom fields The  
      data: {
       type: "object",
       label: "Custom Fields",
-      description: "Additional custom fields to update.",
+      description: "Additional custom fields to update. Example: `{\"customField1\": \"value1\", \"customField2\": \"value2\"}`",
+      additionalProperties: {
+        type: ["string", "number", "boolean"],
+      },
     },📝 Committable suggestion
 
        Suggested change
       
 | ||||||||||||||||||||||||||||||||
| methods: { | ||||||||||||||||||||||||||||||||
| updateDeviceCustomFields({ | ||||||||||||||||||||||||||||||||
| deviceId, ...args | ||||||||||||||||||||||||||||||||
| } = {}) { | ||||||||||||||||||||||||||||||||
| return this.app.patch({ | ||||||||||||||||||||||||||||||||
| path: `/device/${deviceId}/custom-fields`, | ||||||||||||||||||||||||||||||||
| ...args, | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| async run({ $ }) { | ||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||
| updateDeviceCustomFields, | ||||||||||||||||||||||||||||||||
| deviceId, | ||||||||||||||||||||||||||||||||
| data, | ||||||||||||||||||||||||||||||||
| } = this; | ||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||
| const response = await updateDeviceCustomFields({ | ||||||||||||||||||||||||||||||||
| $, | ||||||||||||||||||||||||||||||||
| deviceId, | ||||||||||||||||||||||||||||||||
| data: utils.parse(data), | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||
| $.export("$summary", "Successfully updated device custom fields."); | ||||||||||||||||||||||||||||||||
| return response; | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| const BASE_URL = "https://app.ninjarmm.com"; | ||
| const VERSION_PATH = "/api/v2"; | ||
| const LAST_CREATED_AT = "lastCreatedAt"; | ||
| const DEFAULT_LIMIT = 50; | ||
| const DEFAULT_MAX = 600; | ||
| const WEBHOOK_ID = "webhookId"; | ||
|  | ||
| export default { | ||
| BASE_URL, | ||
| VERSION_PATH, | ||
| DEFAULT_MAX, | ||
| LAST_CREATED_AT, | ||
| DEFAULT_LIMIT, | ||
| WEBHOOK_ID, | ||
| }; | 
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ConfigurationError } from "@pipedream/platform"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function emptyStrToUndefined(value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const trimmed = typeof(value) === "string" && value.trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return trimmed === "" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
      Comment on lines
    
      +4
     to 
      +7
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
        Suggested change
       
 Syntax suggestion using optional chaining | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
      Comment on lines
    
      +3
     to 
      +8
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix type checking and string handling logic. The current implementation has several issues: 
 Here's the corrected implementation:  function emptyStrToUndefined(value) {
-  const trimmed = typeof(value) === "string" && value.trim();
-  return trimmed === ""
-    ? undefined
-    : value;
+  if (value === null || value === undefined) return undefined;
+  if (typeof value !== "string") return value;
+  const trimmed = value.trim();
+  return trimmed === "" ? undefined : value;
 }📝 Committable suggestion
 
        Suggested change
       
 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function parse(value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const valueToParse = emptyStrToUndefined(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof(valueToParse) === "object" || valueToParse === undefined) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return valueToParse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return JSON.parse(valueToParse); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ConfigurationError("Make sure the custom expression contains a valid object."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
      Comment on lines
    
      +10
     to 
      +20
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance error handling and add documentation. The parse function would benefit from: 
 Here's the suggested implementation: +/**
+ * Parses a value into an object, handling empty strings and JSON parsing
+ * @param {any} value - The value to parse
+ * @returns {object|undefined} The parsed object or undefined
+ * @throws {ConfigurationError} If the value is an invalid JSON string
+ */
 function parse(value) {
   const valueToParse = emptyStrToUndefined(value);
   if (typeof(valueToParse) === "object" || valueToParse === undefined) {
     return valueToParse;
   }
   try {
     return JSON.parse(valueToParse);
   } catch (e) {
-    throw new ConfigurationError("Make sure the custom expression contains a valid object.");
+    throw new ConfigurationError(
+      `Invalid JSON format. Expected a valid JSON object, received: ${typeof valueToParse}`
+    );
   }
 }📝 Committable suggestion
 
        Suggested change
       
 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parse, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove or implement commented isDescriptionPublic field
The
isDescriptionPublicfield is commented out in both the destructuring and the request data. Either implement it or remove the comments.And:
Also applies to: 118-118
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be retained as a comment @jcortes ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.