Skip to content

New Components - ninjaone #16755

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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

New Components - ninjaone #16755

wants to merge 6 commits into from

Conversation

luancazarine
Copy link
Collaborator

@luancazarine luancazarine commented May 21, 2025

Resolves #14845.

Summary by CodeRabbit

  • New Features

    • Introduced NinjaOne integration with support for creating support tickets, updating device details, and dynamic property options.
    • Added event sources for NinjaOne, including the ability to emit events when a monitored device comes online.
    • Provided utility functions and standardized constants for NinjaOne actions and sources.
  • Chores

    • Added newlines at the end of several files for improved formatting.
    • Updated NinjaOne package metadata and dependencies.

@luancazarine luancazarine added the ai-assisted Content generated by AI, with human refinement and modification label May 21, 2025
Copy link

vercel bot commented May 21, 2025

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) Visit Preview May 23, 2025 1:05pm
pipedream-docs ⬜️ Ignored (Inspect) May 23, 2025 1:05pm
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) May 23, 2025 1:05pm

Copy link
Contributor

coderabbitai bot commented May 21, 2025

Walkthrough

This update introduces a comprehensive NinjaOne integration, adding new actions for ticket creation and device updates, a polling source for device online events, utility and constants modules, and dynamic property definitions with API pagination. Minor formatting changes were also made to unrelated app files by adding trailing newlines.

Changes

File(s) Change Summary
components/heylibby/heylibby.app.mjs
components/neetocal/neetocal.app.mjs
components/neetodesk/neetodesk.app.mjs
components/scrapecreators/scrapecreators.app.mjs
Added a newline character at the end of each file; no functional code changes.
components/ninjaone/ninjaone.app.mjs Expanded with dynamic propDefinitions, API request methods, pagination, and resource listing for NinjaOne integration.
components/ninjaone/actions/create-ticket/create-ticket.mjs New action to create a support ticket in NinjaOne, with comprehensive input options and structured payload construction.
components/ninjaone/actions/update-device/update-device.mjs New action to update device details in NinjaOne, supporting multiple updatable fields and dynamic option loading.
components/ninjaone/common/constants.mjs New module exporting constants for pagination limits and standardized type, severity, and priority options.
components/ninjaone/common/utils.mjs New utility module exporting parseObject for JSON parsing and normalCase for string formatting.
components/ninjaone/sources/common/base.mjs New polling source base for NinjaOne activities, handling event fetching, deduplication, and emission with persistent state.
components/ninjaone/sources/new-device-online/new-device-online.mjs New polling source for emitting events when a device comes online (system rebooted), with filtering and summary generation.
components/ninjaone/sources/new-device-online/test-event.mjs New test event object simulating a device online (reboot) event for testing the polling source.
components/ninjaone/package.json Updated version to 0.1.0, added dependency on @pipedream/platform, and adjusted JSON structure.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant NinjaOne Action (Create Ticket)
  participant NinjaOne App
  participant NinjaOne API

  User->>NinjaOne Action (Create Ticket): Provide ticket details
  NinjaOne Action (Create Ticket)->>NinjaOne App: Call createSupportTicket()
  NinjaOne App->>NinjaOne API: POST /tickets with payload
  NinjaOne API-->>NinjaOne App: Response (ticket ID)
  NinjaOne App-->>NinjaOne Action (Create Ticket): Return result
  NinjaOne Action (Create Ticket)-->>User: Output summary
Loading
sequenceDiagram
  participant Polling Source
  participant NinjaOne App
  participant NinjaOne API
  participant User

  Polling Source->>NinjaOne App: listActivities({ newerThan: lastId })
  NinjaOne App->>NinjaOne API: GET /activities
  NinjaOne API-->>NinjaOne App: Return activities
  NinjaOne App-->>Polling Source: Activities list
  Polling Source->>User: Emit new device online events
Loading
sequenceDiagram
  participant User
  participant NinjaOne Action (Update Device)
  participant NinjaOne App
  participant NinjaOne API

  User->>NinjaOne Action (Update Device): Provide device ID and update fields
  NinjaOne Action (Update Device)->>NinjaOne App: Call updateDevice()
  NinjaOne App->>NinjaOne API: PATCH /devices/{deviceId}
  NinjaOne API-->>NinjaOne App: Response
  NinjaOne App-->>NinjaOne Action (Update Device): Return result
  NinjaOne Action (Update Device)-->>User: Output summary
Loading

Assessment against linked issues

Objective Addressed Explanation
Implement NinjaOne polling source: new-device-online (device online event, optional group/type filter) (#14845)
Implement NinjaOne action: create-ticket (title, description, priority, optional assignment/due date) (#14845)
Implement NinjaOne action: update-device (device identifier, updatable attributes) (#14845)
Dynamic property loading, API pagination, and error handling for NinjaOne integration (#14845)

Poem

In burrows deep, a rabbit spies
New NinjaOne actions—what a surprise!
Tickets created, devices updated,
Online events now celebrated.
With constants and utils, the code is neat—
This patch is a carrot-flavored treat! 🥕

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

components/neetodesk/neetodesk.app.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:799:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:723:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:706:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38)
at #link (node:internal/modules/esm/module_job:163:49)

components/ninjaone/common/constants.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:799:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:723:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:706:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38)
at #link (node:internal/modules/esm/module_job:163:49)

components/ninjaone/actions/update-device/update-device.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:799:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:723:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:706:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38)
at #link (node:internal/modules/esm/module_job:163:49)

  • 9 others

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @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.

@luancazarine luancazarine marked this pull request as ready for review May 23, 2025 13:06
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: 5

🧹 Nitpick comments (3)
components/ninjaone/actions/update-device/update-device.mjs (1)

16-16: Fix trailing space in description.

There's a trailing space at the end of the description that should be removed for consistency.

-      description: "The Id of the device to update ",
+      description: "The Id of the device to update",
components/ninjaone/common/constants.mjs (1)

1-23: Constants are well-defined and organized.

The exported constants provide appropriate options for ticket classification and a reasonable pagination limit. The values align with standard ticket management system categories.

Consider adding a newline at the end of the file for consistency with standard formatting practices.

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

33-47: Consider adding pagination support for consistency.

The implementation is correct, but unlike other prop definitions, this one doesn't support pagination. If the number of ticket forms can grow large, consider adding pagination support for consistency and scalability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb039e0 and 1517d45.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • components/heylibby/heylibby.app.mjs (1 hunks)
  • components/neetocal/neetocal.app.mjs (1 hunks)
  • components/neetodesk/neetodesk.app.mjs (1 hunks)
  • components/ninjaone/actions/create-ticket/create-ticket.mjs (1 hunks)
  • components/ninjaone/actions/update-device/update-device.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/base.mjs (1 hunks)
  • components/ninjaone/sources/new-device-online/new-device-online.mjs (1 hunks)
  • components/ninjaone/sources/new-device-online/test-event.mjs (1 hunks)
  • components/scrapecreators/scrapecreators.app.mjs (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
components/ninjaone/ninjaone.app.mjs

[error] 318-318: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
🔇 Additional comments (18)
components/neetocal/neetocal.app.mjs (1)

11-11: Trailing newline addition – purely formatting
This change adds a newline at EOF for consistency with other component files. No functional or logical code was modified.

components/neetodesk/neetodesk.app.mjs (1)

11-11: Trailing newline addition – purely formatting
This change adds a newline at EOF for consistency with other component files. No functional or logical code was modified.

components/heylibby/heylibby.app.mjs (1)

11-11: Trailing newline addition – purely formatting
This change adds a newline at EOF for consistency with other component files. No functional or logical code was modified.

components/scrapecreators/scrapecreators.app.mjs (1)

11-11: Trailing newline addition – purely formatting
This change adds a newline at EOF for consistency with other component files. No functional or logical code was modified.

components/ninjaone/package.json (1)

3-17: LGTM! Package configuration updated correctly.

The version bump to 0.1.0 appropriately reflects the new feature additions, and the @pipedream/platform dependency is correctly added for the new NinjaOne components.

components/ninjaone/sources/new-device-online/test-event.mjs (1)

1-18: Well-structured test event data.

The test event properly represents a device reboot event with realistic data structure, appropriate timestamp formatting, and nested message parameters that would be typical for NinjaOne API responses.

components/ninjaone/actions/update-device/update-device.mjs (1)

56-71: Action implementation looks solid.

The run method properly destructures props, makes the API call with correct parameters, and provides informative feedback. The error handling is delegated to the underlying ninjaone app method, which is appropriate.

components/ninjaone/common/utils.mjs (1)

1-24: LGTM! Robust JSON parsing utility.

The parseObject function handles various input types well with proper error handling for JSON parsing failures. The defensive programming approach with falsy checks and type validation is excellent.

components/ninjaone/sources/common/base.mjs (2)

56-64: Good deployment and execution pattern.

The deploy hook with a 25-event limit for initial setup and unlimited polling in the run method follows best practices for polling sources.


22-54:

❓ Verification inconclusive

Verify the API response ordering assumption.

The implementation assumes the API returns activities in newest-first order (evidenced by setting lastId to responseArray[0].id and then reversing before emission). Please ensure this matches the actual NinjaOne API behavior.

Run this script to verify the API response ordering:


🏁 Script executed:

#!/bin/bash
# Description: Search for NinjaOne API documentation or implementation details about activity ordering

# Look for any documentation about listActivities method ordering
rg -A 10 -B 5 "listActivities|activity.*order|newer.*than" --type js

# Search for any API response handling that might indicate ordering
ast-grep --pattern 'listActivities($$$) {
  $$$
}'

Length of output: 123


Verify the NinjaOne API response ordering assumption

I wasn’t able to locate any definitive reference to the ordering of listActivities in the codebase. Before relying on responseArray[0].id as the newest event, please:

  • Check the official NinjaOne API documentation (or contact support) to confirm that listActivities returns activities sorted newest-first.
  • If it actually returns oldest-first, update the lastId assignment to use the last element in the array instead.

Ensuring this aligns with the API will prevent missing or duplicating events in your polling logic.

components/ninjaone/sources/new-device-online/new-device-online.mjs (1)

4-27: Well-structured source component following good extension patterns.

The implementation properly extends the base polling source with appropriate filtering for device reboot events. The method overrides are clean and focused on the specific use case.

components/ninjaone/actions/create-ticket/create-ticket.mjs (2)

19-141: Excellent prop definition structure leveraging dynamic options.

The comprehensive use of propDefinition for dynamic options and the logical grouping of ticket properties demonstrates good component design patterns.


142-180: Robust ticket creation logic with proper error handling.

The implementation properly structures the nested data objects, uses utility functions for data parsing, and provides clear error messaging through the ConfigurationError pattern.

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

1-3: LGTM!

The import statements are clean and properly structured.


8-32: LGTM!

The clientId prop definition correctly implements pagination and maps organization data to options.


48-75: LGTM!

The locationId prop definition correctly implements organization-specific filtering and pagination.


134-152: LGTM!

The assignedAppUserId prop definition correctly filters for technicians and provides descriptive labels.


210-226: LGTM!

The base utility methods follow best practices for API client implementation.

Comment on lines +26 to +28
export const normalCase = (s) =>
s?.replace (/^[-_]*(.)/, (_, c) => c.toUpperCase())
.replace (/[-_]+(.)/g, (_, c) => " " + c);
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 the normalCase function to properly capitalize characters after spaces.

The second replace operation doesn't capitalize the character after the space, which means "kebab-case" would become "Kebab case" instead of the expected "Kebab Case".

Apply this diff to fix the capitalization:

export const normalCase = (s) =>
  s?.replace (/^[-_]*(.)/, (_, c) => c.toUpperCase())
-    .replace (/[-_]+(.)/g, (_, c) => " " + c);
+    .replace (/[-_]+(.)/g, (_, c) => " " + c.toUpperCase());
📝 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
export const normalCase = (s) =>
s?.replace (/^[-_]*(.)/, (_, c) => c.toUpperCase())
.replace (/[-_]+(.)/g, (_, c) => " " + c);
export const normalCase = (s) =>
s?.replace(/^[-_]*(.)/, (_, c) => c.toUpperCase())
.replace(/[-_]+(.)/g, (_, c) => " " + c.toUpperCase());
🤖 Prompt for AI Agents
In components/ninjaone/common/utils.mjs around lines 26 to 28, the normalCase
function fails to capitalize characters following spaces after replacing hyphens
or underscores. Modify the second replace callback to convert the matched
character to uppercase, ensuring that each word after a space starts with a
capital letter, so "kebab-case" becomes "Kebab Case" as expected.

Comment on lines +168 to +182
policyId: {
type: "string",
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,
}));
},
},
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 typo in description.

There's a typo in the description field.

 policyId: {
   type: "string",
   label: "Policy Id",
-  description: "The id of the policiy override",
+  description: "The id of the policy override",
   async options() {
📝 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
policyId: {
type: "string",
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,
}));
},
},
policyId: {
type: "string",
label: "Policy Id",
description: "The id of the policy override",
async options() {
const data = await this.listPolicies();
return data.map(({
id: value, name: label,
}) => ({
label,
value,
}));
},
},
🤖 Prompt for AI Agents
In components/ninjaone/ninjaone.app.mjs around lines 168 to 182, fix the typo in
the description field of the policyId object by correcting "policiy" to "policy"
to ensure the description reads "The id of the policy override".

Comment on lines +246 to +261
listLocations({
organizationId, ...opts
}) {
return this._makeRequest({
path: `organization/${organizationId}/locations`,
...opts,
});
},
listDevices({
organizationId, ...opts
}) {
return this._makeRequest({
path: `organization/${organizationId}/devices`,
...opts,
});
},
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 missing leading slash in API paths.

The listLocations and listDevices methods are missing the leading slash in their paths, which could cause API calls to fail.

 listLocations({
   organizationId, ...opts
 }) {
   return this._makeRequest({
-    path: `organization/${organizationId}/locations`,
+    path: `/organization/${organizationId}/locations`,
     ...opts,
   });
 },
 listDevices({
   organizationId, ...opts
 }) {
   return this._makeRequest({
-    path: `organization/${organizationId}/devices`,
+    path: `/organization/${organizationId}/devices`,
     ...opts,
   });
 },
📝 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
listLocations({
organizationId, ...opts
}) {
return this._makeRequest({
path: `organization/${organizationId}/locations`,
...opts,
});
},
listDevices({
organizationId, ...opts
}) {
return this._makeRequest({
path: `organization/${organizationId}/devices`,
...opts,
});
},
listLocations({
organizationId, ...opts
}) {
return this._makeRequest({
path: `/organization/${organizationId}/locations`,
...opts,
});
},
listDevices({
organizationId, ...opts
}) {
return this._makeRequest({
path: `/organization/${organizationId}/devices`,
...opts,
});
},
🤖 Prompt for AI Agents
In components/ninjaone/ninjaone.app.mjs around lines 246 to 261, the API paths
in listLocations and listDevices methods lack a leading slash, which may cause
incorrect URL formation. Add a leading slash to the path strings in both
methods, changing `organization/${organizationId}/locations` to
`/organization/${organizationId}/locations` and similarly for devices, to ensure
correct API endpoint URLs.

Comment on lines +104 to +118
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);
},
},
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 missing pagination context in tags options.

The tags prop definition returns only the tag names array without the pagination context, which will break pagination functionality.

Apply this fix to include the pagination context:

 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);
+    return {
+      options: tags.map(({ name }) => name),
+      context: {
+        lastId: tags[tags.length - 1]?.id || tags[tags.length - 1]?.name,
+      },
+    };
   },
 },

Note: The lastId might need to be adjusted based on the actual tag object structure returned by the 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
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);
},
},
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 {
options: tags.map(({ name }) => name),
context: {
lastId: tags[tags.length - 1]?.id || tags[tags.length - 1]?.name,
},
};
},
},
🤖 Prompt for AI Agents
In components/ninjaone/ninjaone.app.mjs around lines 104 to 118, the tags
options function returns only the array of tag names but omits the pagination
context needed for proper pagination. Modify the return value to include both
the array of tag names and the updated pagination context, typically by
returning an object with a 'results' property for the tag names and a 'context'
property containing the last tag ID from the current page. Adjust the extraction
of lastId based on the actual tag object structure to ensure correct pagination
continuation.

Comment on lines +307 to 336
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);
},
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 critical issues in the paginate method.

The paginate method has several issues that need to be addressed:

  1. The delete operator can impact performance (line 318)
  2. The lastId variable is never updated in the loop
  3. The hasMore condition logic appears incorrect
 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;
+      params.newerThan = undefined;
     }
 
     const { activities } = await fn({
       params,
       ...opts,
     });
     for (const d of activities) {
       yield d;
+      lastId = d.id;
 
       if (maxResults && ++count === maxResults) {
         return count;
       }
     }
 
-    hasMore = activities.length && (lastId && (lastId > newerThan));
+    hasMore = activities.length > 0;
 
   } while (hasMore);
 },
📝 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
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);
},
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;
params.newerThan = undefined;
}
const { activities } = await fn({
params,
...opts,
});
for (const d of activities) {
yield d;
lastId = d.id;
if (maxResults && ++count === maxResults) {
return count;
}
}
hasMore = activities.length > 0;
} while (hasMore);
},
🧰 Tools
🪛 Biome (1.9.4)

[error] 318-318: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🤖 Prompt for AI Agents
In components/ninjaone/ninjaone.app.mjs lines 307 to 336, fix the paginate
method by avoiding the use of the delete operator on params.newerThan; instead,
create a new params object without newerThan when needed. Update lastId inside
the loop to the ID of the last activity processed to correctly paginate. Correct
the hasMore condition to check if activities length is non-zero and if lastId is
less than newerThan or based on the actual pagination logic required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ai-assisted Content generated by AI, with human refinement and modification
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Components] ninjaone
1 participant