-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
base: master
Are you sure you want to change the base?
New Components - ninjaone #16755
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 3 Skipped Deployments
|
WalkthroughThis 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
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
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
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
Assessment against linked issues
Poem
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
components/neetodesk/neetodesk.app.mjsOops! Something went wrong! :( ESLint: 8.57.1 Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs components/ninjaone/common/constants.mjsOops! Something went wrong! :( ESLint: 8.57.1 Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs components/ninjaone/actions/update-device/update-device.mjsOops! Something went wrong! :( ESLint: 8.57.1 Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
Note ⚡️ AI Code Reviews for VS Code, Cursor, WindsurfCodeRabbit 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. ✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Sources - New Device Online (Instant) Actions - Create Ticket - Update Device
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.
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
⛔ 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
toresponseArray[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 onresponseArray[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.
export const normalCase = (s) => | ||
s?.replace (/^[-_]*(.)/, (_, c) => c.toUpperCase()) | ||
.replace (/[-_]+(.)/g, (_, c) => " " + c); |
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.
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.
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.
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, | ||
})); | ||
}, | ||
}, |
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.
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.
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".
listLocations({ | ||
organizationId, ...opts | ||
}) { | ||
return this._makeRequest({ | ||
path: `organization/${organizationId}/locations`, | ||
...opts, | ||
}); | ||
}, | ||
listDevices({ | ||
organizationId, ...opts | ||
}) { | ||
return this._makeRequest({ | ||
path: `organization/${organizationId}/devices`, | ||
...opts, | ||
}); | ||
}, |
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.
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.
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.
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); | ||
}, | ||
}, |
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.
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.
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.
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); | ||
}, |
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.
Fix critical issues in the paginate method.
The paginate method has several issues that need to be addressed:
- The
delete
operator can impact performance (line 318) - The
lastId
variable is never updated in the loop - 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.
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.
Resolves #14845.
Summary by CodeRabbit
New Features
Chores