Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions components/ninjaone/actions/create-ticket/create-ticket.mjs
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,
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove or implement commented isDescriptionPublic field

The isDescriptionPublic field is commented out in both the destructuring and the request data. Either implement it or remove the comments.

-      // isDescriptionPublic,
+      isDescriptionPublic,

And:

-        // isDescriptionPublic,
+        isDescriptionPublic,

Also applies to: 118-118

Copy link
Collaborator

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 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

parentTicketId,
priority,
} = this;

const response = await createTicket({
$,
data: {
clientId,
ticketFormId,
subject,
status,
type,
assignedAppUserId,
description,
// isDescriptionPublic,
parentTicketId,
priority,
},
});

$.export("$summary", "Successfully created ticket.");
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation and examples for custom fields

The data property could benefit from:

  1. Schema validation for custom fields
  2. Example in the description
     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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data: {
type: "object",
label: "Custom Fields",
description: "Additional custom fields to update.",
},
},
data: {
type: "object",
label: "Custom Fields",
description: "Additional custom fields to update. Example: `{\"customField1\": \"value1\", \"customField2\": \"value2\"}`",
additionalProperties: {
type: ["string", "number", "boolean"],
},
},
},

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;
},
};
15 changes: 15 additions & 0 deletions components/ninjaone/common/constants.mjs
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,
};
24 changes: 24 additions & 0 deletions components/ninjaone/common/utils.mjs
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const trimmed = typeof(value) === "string" && value.trim();
return trimmed === ""
? undefined
: value;
return value?.trim?.() === "" ? undefined : value;

Syntax suggestion using optional chaining

}
Comment on lines +3 to +8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix type checking and string handling logic.

The current implementation has several issues:

  1. Incorrect typeof operator syntax (should be typeof value)
  2. The trimmed value calculation is not properly utilized
  3. No handling for null values

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function emptyStrToUndefined(value) {
const trimmed = typeof(value) === "string" && value.trim();
return trimmed === ""
? undefined
: value;
}
function emptyStrToUndefined(value) {
if (value === null || value === undefined) return undefined;
if (typeof value !== "string") return value;
const trimmed = value.trim();
return trimmed === "" ? undefined : value;
}


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
Copy link
Contributor

Choose a reason for hiding this comment

The 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:

  1. More descriptive error messages
  2. JSDoc documentation
  3. Additional type checking

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

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.");
}
}
/**
* 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(
`Invalid JSON format. Expected a valid JSON object, received: ${typeof valueToParse}`
);
}
}


export default {
parse,
};
Loading
Loading