Skip to content
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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

[Components] ninjaone - WIP #14892

wants to merge 1 commit into from

Conversation

jcortes
Copy link
Collaborator

@jcortes jcortes commented Dec 9, 2024

WHY

Resolves #14845

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced support ticket creation functionality.
    • Added the ability to update custom fields for devices.
    • New webhook management capabilities for activating and deactivating webhooks.
    • Event handling for devices coming online and initiating remote sessions.
    • Event emission for the creation of new support tickets.
  • Improvements

    • Enhanced API interaction methods for better data retrieval and management.
    • Added constants for easier configuration management across the application.
  • Version Update

    • Updated package version to 0.1.0.

@jcortes jcortes added action New Action Request trigger / source New trigger / source request labels Dec 9, 2024
@jcortes jcortes self-assigned this Dec 9, 2024
Copy link

vercel bot commented Dec 9, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs-v2 ⬜️ Ignored (Inspect) Dec 9, 2024 5:55pm
pipedream-docs ⬜️ Ignored (Inspect) Dec 9, 2024 5:55pm
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Dec 9, 2024 5:55pm

Copy link
Contributor

coderabbitai bot commented Dec 9, 2024

Walkthrough

This pull request introduces multiple new modules and enhancements to the NinjaOne application. Key additions include actions for creating support tickets and updating device custom fields, as well as new event sources for monitoring device online status and remote session initiation. A constants module is also added to centralize configuration values. Furthermore, utility functions for string and JSON parsing are introduced. The ninjaone.app.mjs file is significantly updated to include new properties and methods for API interactions, enhancing the application's functionality.

Changes

File Path Change Summary
components/ninjaone/actions/create-ticket/create-ticket.mjs New action ninjaone-create-ticket with properties and a method for creating support tickets.
components/ninjaone/actions/update-device-custom-fields/update-device-custom-fields.mjs New action ninjaone-update-device-custom-fields for updating device custom fields with a PATCH request.
components/ninjaone/common/constants.mjs New constants module defining various application-wide constants.
components/ninjaone/common/utils.mjs New utility functions for handling string and JSON parsing operations.
components/ninjaone/ninjaone.app.mjs Enhanced propDefinitions and new methods for API interactions, including organization and ticket management.
components/ninjaone/sources/common/webhook.mjs New module for managing webhooks with methods for activation and deactivation.
components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs New event source for device online events with methods for event name and metadata generation.
components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs New event source for remote session initiation with methods for event name and metadata generation.
components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs New event source for new ticket creation events with methods for event name and metadata generation.
components/ninjaone/package.json Version updated to 0.1.0 and added dependency on @pipedream/platform.

Assessment against linked issues

Objective Addressed Explanation
Emit new event when a new support ticket is created in NinjaOne. (#14845)
Emit new event when a monitored device comes online. (#14845)
Emit new event when a remote access session is initiated. (#14845)
Create a new support ticket in NinjaOne. (#14845)
Update details for a specific device in NinjaOne. (#14845)

Possibly related issues

  • [Components] ninjaone #14844: This issue also discusses emitting new events when a support ticket is created, which aligns with the new ticket instant functionality introduced in this PR.

Possibly related PRs

Suggested reviewers

  • michelle0927

Poem

🐰 In the land of NinjaOne, where tickets do fly,
New events spring forth, oh me, oh my!
Devices come online, sessions take flight,
With constants and utils, our code shines bright!
So hop along, dear friends, let’s celebrate today,
For with these changes, we’re paving the way! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (10)
components/ninjaone/sources/common/webhook.mjs (1)

60-65: Clarify purpose of methods throwing ConfigurationError

The methods getResourcesFn and getResourcesFnArgs throw ConfigurationError by default. If these methods are intended to be abstract and meant for overriding in subclasses, consider adding documentation or comments to make this clear.

components/ninjaone/ninjaone.app.mjs (2)

8-112: Refactor pagination logic into a shared utility function

The options methods for organizationId (lines 12-35) and deviceId (lines 87-111) share similar pagination logic. To improve maintainability and reduce code duplication, consider refactoring this logic into a shared utility function that can be reused across these methods.


124-133: Handle potential errors in _makeRequest method

The _makeRequest method does not include error handling for API request failures. To enhance robustness, consider adding error handling to manage cases where the API might return errors or fail to respond.

components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs (1)

17-23: Improve resource metadata generation.

The current implementation uses a generic "New Resource" message. Consider making it more specific to tickets for better clarity.

   generateMeta(resource) {
     return {
       id: resource.id,
-      summary: `New Resource: ${resource.name}`,
+      summary: `New Ticket: ${resource.name || resource.title}`,
       ts: Date.parse(resource.created_at),
     };
   }
components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs (1)

1-25: Consider creating a base source factory to reduce code duplication.

The implementation is nearly identical to new-ticket-instant, differing only in key, name, and description. Consider creating a factory function to generate these source components.

Example implementation:

// components/ninjaone/sources/base/sourceFactory.mjs
export function createSource({
  key,
  name,
  description,
}) {
  return {
    ...common,
    key,
    name,
    description,
    version: "0.0.1",
    type: "source",
    dedupe: "unique",
    methods: {
      ...common.methods,
      getEventName() {
        return events.DEFAULT;
      },
      generateMeta(resource) {
        return {
          id: resource.id,
          summary: `${name}: ${resource.name}`,
          ts: Date.parse(resource.created_at),
        };
      },
    },
  };
}

This would reduce duplication and make it easier to maintain consistent behavior across sources.

components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs (3)

7-8: Enhance component description with filtering capabilities

The description should mention the optional filters for session type or technician as specified in the PR objectives.

-  description: "Emit new event when a remote access session is initiated. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core#/webhooks/configureWebhook).",
+  description: "Emit new event when a remote access session is initiated. Optionally filter by session type or technician. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core#/webhooks/configureWebhook).",

14-16: Consider using a more specific event name

Using the DEFAULT event name might be too generic. Consider defining a specific event name for remote sessions in the events module.

-      return events.DEFAULT;
+      return events.REMOTE_SESSION_STARTED;

17-23: Enhance metadata summary with session details

The summary could be more descriptive by including session type or technician information if available.

-        summary: `New Resource: ${resource.name}`,
+        summary: `New Remote Session: ${resource.type || ''} by ${resource.technician || 'Unknown'}`,
components/ninjaone/actions/update-device-custom-fields/update-device-custom-fields.mjs (1)

47-48: Enhance success message with update details

The success message could be more informative by including the device ID and number of fields updated.

-    $.export("$summary", "Successfully updated device custom fields.");
+    $.export("$summary", `Successfully updated ${Object.keys(utils.parse(data)).length} custom fields for device ${deviceId}.`);
components/ninjaone/actions/create-ticket/create-ticket.mjs (1)

124-124: Enhance success message with ticket details

Include the ticket ID and subject in the success message for better traceability.

-    $.export("$summary", "Successfully created ticket.");
+    $.export("$summary", `Successfully created ticket #${response.id}: ${subject}`);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 330ef84 and d8a98af.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • components/ninjaone/actions/create-ticket/create-ticket.mjs (1 hunks)
  • components/ninjaone/actions/update-device-custom-fields/update-device-custom-fields.mjs (1 hunks)
  • components/ninjaone/common/constants.mjs (1 hunks)
  • components/ninjaone/common/utils.mjs (1 hunks)
  • components/ninjaone/ninjaone.app.mjs (1 hunks)
  • components/ninjaone/package.json (2 hunks)
  • components/ninjaone/sources/common/webhook.mjs (1 hunks)
  • components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs (1 hunks)
  • components/ninjaone/sources/new-remote-session-instant/new-remote-session-instant.mjs (1 hunks)
  • components/ninjaone/sources/new-ticket-instant/new-ticket-instant.mjs (1 hunks)
🔇 Additional comments (2)
components/ninjaone/common/constants.mjs (1)

1-15: Constants are well-defined and improve code maintainability

The defined constants provide a central place for configuration values, which enhances code readability and maintainability.

components/ninjaone/package.json (1)

16-17: Verify that @pipedream/platform dependency is up-to-date and secure

Ensure that the version "3.0.3" of @pipedream/platform is the latest stable release and that there are no known security vulnerabilities associated with it.

Run the following script to check for the latest version:

✅ Verification successful

@pipedream/platform dependency is up-to-date

The package is using the latest stable version (3.0.3) available on npm registry.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the latest version of @pipedream/platform on npm

# Fetch the latest version from the npm registry
curl -s https://registry.npmjs.org/@pipedream/platform | jq -r '."dist-tags".latest'

Length of output: 92

Comment on lines +48 to +50
generateMeta() {
throw new ConfigurationError("generateMeta is not implemented");
},
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

Ensure generateMeta is implemented in subclasses to prevent runtime errors

The generateMeta method currently throws a ConfigurationError. If not overridden in subclasses, this will cause runtime errors when processResource is called in the run method. Please ensure that all subclasses implement this method appropriately.

Comment on lines +57 to +59
getEventName() {
throw new ConfigurationError("getEventName is not implemented");
},
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

Ensure getEventName is implemented in subclasses to avoid errors during webhook activation

The getEventName method throws a ConfigurationError by default. This method is called in the activate hook when creating a webhook. If not implemented in subclasses, it will result in runtime errors during activation. Please ensure this method is properly implemented in all subclasses.

Comment on lines +10 to +20
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.");
}
}
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}`
);
}
}

Comment on lines +3 to +8
function emptyStrToUndefined(value) {
const trimmed = typeof(value) === "string" && value.trim();
return trimmed === ""
? undefined
: value;
}
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;
}

Comment on lines +4 to +25
export default {
...common,
key: "ninjaone-new-device-online-instant",
name: "New Device Online (Instant)",
description: "Emit new event when a monitored device comes online. [See the documentation](https://app.ninjarmm.com/apidocs/?links.active=core#/webhooks/configureWebhook).",
version: "0.0.1",
type: "source",
dedupe: "unique",
methods: {
...common.methods,
getEventName() {
return events.DEFAULT;
},
generateMeta(resource) {
return {
id: resource.id,
summary: `New Resource: ${resource.name}`,
ts: Date.parse(resource.created_at),
};
},
},
};
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the webhook common implementation I can now provide the final response:

Optional filters for device groups and types are missing from the implementation

The current webhook implementation only provides basic webhook functionality without the mentioned filter options. The common webhook props only include:

  • app connection
  • database interface
  • HTTP webhook interface

Required changes:

  • Add props for device group filter
  • Add props for device type filter
  • Update webhook creation to include these filters in the request payload
🔗 Analysis chain

Verify webhook configuration options against requirements.

The PR objectives mention optional filters for device groups and types, but the current implementation doesn't show how these filters are configured.


Let me try a different search strategy to verify the webhook configuration and filter implementation.


Let me try one more search to locate the webhook implementation and filter configuration.


Let me check the webhook common implementation since we found its location.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for filter configuration in common webhook implementation
echo "Searching for filter configuration in webhook implementation..."
rg -l "filter" --type js components/ninjaone/common/

# Check if props are defined for filters
echo "Checking for filter-related props..."
ast-grep --pattern 'props: {
  $$$
}' components/ninjaone/sources/

Length of output: 341


Script:

#!/bin/bash
# Search for webhook configuration in common implementation
echo "Checking common webhook implementation..."
rg -A 10 "webhook" components/ninjaone/common/

# Search for props or configuration in the source file
echo "Checking source implementation..."
cat components/ninjaone/sources/new-device-online-instant/new-device-online-instant.mjs

# Check common.js content
echo "Checking common.js content..."
cat components/ninjaone/common/webhook.mjs

Length of output: 1765


Script:

#!/bin/bash
# Find the webhook common file
echo "Looking for webhook common file..."
fd webhook.mjs components/ninjaone/

# Check events implementation
echo "Checking events implementation..."
fd events.mjs components/ninjaone/ --exec cat {}

# Check for any prop definitions in source files
echo "Checking for prop definitions in source files..."
rg "props: {" --type js components/ninjaone/ -A 10

Length of output: 533


Script:

#!/bin/bash
# Check webhook common implementation
echo "Checking webhook common implementation..."
cat components/ninjaone/sources/common/webhook.mjs

# Fix the props search with proper escaping
echo "Checking for props definitions..."
rg "props:\s*{" --type js components/ninjaone/ -A 10

Length of output: 2344

Comment on lines +18 to +23
data: {
type: "object",
label: "Custom Fields",
description: "Additional custom fields to update.",
},
},
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"],
},
},
},

type,
assignedAppUserId,
description,
// isDescriptionPublic,
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

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

Comment on lines +108 to +123
const response = await createTicket({
$,
data: {
clientId,
ticketFormId,
subject,
status,
type,
assignedAppUserId,
description,
// isDescriptionPublic,
parentTicketId,
priority,
},
});

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 error handling and input validation

The ticket creation could benefit from:

  1. Error handling for API responses
  2. Validation for required fields before making the request
+    // Validate required fields
+    if (!subject || !description) {
+      throw new Error("Subject and description are required fields");
+    }
+
     const response = await createTicket({
       $,
       data: {
         clientId,
         ticketFormId,
         subject,
         status,
         type,
         assignedAppUserId,
         description,
         parentTicketId,
         priority,
       },
     });
+
+    if (!response?.id) {
+      throw new Error("Failed to create ticket: Invalid response from API");
+    }
📝 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
const response = await createTicket({
$,
data: {
clientId,
ticketFormId,
subject,
status,
type,
assignedAppUserId,
description,
// isDescriptionPublic,
parentTicketId,
priority,
},
});
// Validate required fields
if (!subject || !description) {
throw new Error("Subject and description are required fields");
}
const response = await createTicket({
$,
data: {
clientId,
ticketFormId,
subject,
status,
type,
assignedAppUserId,
description,
// isDescriptionPublic,
parentTicketId,
priority,
},
});
if (!response?.id) {
throw new Error("Failed to create ticket: Invalid response from API");
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
action New Action Request trigger / source New trigger / source request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Components] ninjaone
1 participant