Skip to content

NinjaOne LCP#34

Open
Deenk wants to merge 37 commits intomainfrom
ninjaone
Open

NinjaOne LCP#34
Deenk wants to merge 37 commits intomainfrom
ninjaone

Conversation

@Deenk
Copy link
Copy Markdown

@Deenk Deenk commented Apr 7, 2026

🔌 Plugin overview

  • Plugin name: NinjaOne
  • Purpose / problem solved: Enable squaredup users to monitor devices, endpoints, tickets via NinjaOne RMM
  • Primary audience (e.g. platform teams, SREs, product teams): Systems and helpdesk (Internal and MSP)
  • Authentication method(s) (e.g. OAuth, Username/Password, API Key): Oauth client secret and id

🖼️ Plugin screenshots

Plugin configuration

image image

Default dashboards

image

🧪 Testing

I have tested that every data stream returns unnested arrays and results. data types should all be correct for strings, numbers, dates


⚠️ Known limitations

There are a number of ninjaone modules/add ons based on the licence tier of the customer. I already support the backups and tickets modules, but there are 5/6 more I could add support for later.


📚 Checklist

  • Plugin, datastream and UI naming follow SquaredUp guidelines
  • Logo added
  • One or more dashboards added
  • README added including configuration guidance
  • No secrets or credentials included
  • I agree to the Code of Conduct

@Deenk Deenk added the new-plugin Used to PR newly added plugins label Apr 7, 2026
@Deenk Deenk marked this pull request as ready for review April 7, 2026 08:38
Comment thread plugins/NinjaOne/v1/metadata.json
Comment thread plugins/NinjaOne/v1/dataStreams/organizationDevices.json Outdated
Comment thread plugins/NinjaOne/v1/defaultContent/backupManagement.dash.json Outdated
Comment thread plugins/NinjaOne/v1/ui.json Outdated
Comment thread plugins/NinjaOne/v1/dataStreams/activities.json
@Deenk Deenk requested a review from clarkd April 7, 2026 10:14
@gdorward
Copy link
Copy Markdown

gdorward commented Apr 7, 2026

@Deenk , I seem to be running into repeated 500 errors on the Backup Management OOB dashboards. Looking at the logs, one of the offending endpoints seems to be https://ca-api.ninjarmm.com/v2/queries/backup/usage?pageSize=1000

ERROR {"message":"handler.readDataSource threw: 500 - Internal Server Error"

@AliceG-Sq AliceG-Sq self-requested a review April 8, 2026 09:06
@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 8, 2026

@Deenk , I seem to be running into repeated 500 errors on the Backup Management OOB dashboards. Looking at the logs, one of the offending endpoints seems to be https://ca-api.ninjarmm.com/v2/queries/backup/usage?pageSize=1000

ERROR {"message":"handler.readDataSource threw: 500 - Internal Server Error"

@gdorward This is probably because our license expired for the 'backups' module in NinjaOne. I'll investigate if that truly is the case and either request they fix it, or fix whatever is broken on our side depending on who's fault it is

@Deenk Deenk self-assigned this Apr 8, 2026
@gdorward
Copy link
Copy Markdown

gdorward commented Apr 9, 2026

I'm getting this error on imports all of a sudden @Deenk - specifically importing devices:

{ "name": "Error", "message": "ID column 'deviceId' not found in data stream response. Available columns: results.0.id, results.0.uid, results.0.organizationId, results.0.locationId, results.0.nodeClass, results.0.nodeRoleId, results.0.rolePolicyId, results.0.approvalStatus, results.0.offline, results.0.systemName, results.0.dnsName, results.0.created, results.0.lastContact, results.0.lastUpdate, results.0.deviceId, results.0.displayName, results.1.id, results.1.uid, results.1.organizationId, results.1.locationId, results.1.nodeClass, results.1.nodeRoleId, results.1.rolePolicyId, results.1.approvalStatus, results.1.offline, results.1.systemName, results.1.dnsName, results.1.created, results.1.lastContact, results.1.lastUpdate, results.1.deviceId, results.1.displayName, metadata.next_page_token", "stack": "Error: ID column 'deviceId' not found in data stream response. Available columns: results.0.id, results.0.uid, results.0.organizationId, results.0.locationId, results.0.nodeClass, results.0.nodeRoleId, results.0.rolePolicyId, results.0.approvalStatus, results.0.offline, results.0.systemName, results.0.dnsName, results.0.created, results.0.lastContact, results.0.lastUpdate, results.0.deviceId, results.0.displayName, results.1.id, results.1.uid, results.1.organizationId, results.1.locationId, results.1.nodeClass, results.1.nodeRoleId, results.1.rolePolicyId, results.1.approvalStatus, results.1.offline, results.1.systemName, results.1.dnsName, results.1.created, results.1.lastContact, results.1.lastUpdate, results.1.deviceId, results.1.displayName, metadata.next_page_token at dUe (/src/stepExecutor/mapStreamDataToVertices.ts:62:15) at E1r (/src/stepExecutor/handler.ts:172:33) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async <anonymous> (/src/createMessageHandler.ts:74:13) at async Promise.all (index 0) at async <anonymous> (/src/createMessageHandler.ts:121:32) at async Hcr (/node_modules/.pnpm/@middy+core@2.5.7/node_modules/@middy/core/index.js:86:26)" }

@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 9, 2026

I'm getting this error on imports all of a sudden @Deenk - specifically importing devices:

{ "name": "Error", "message": "ID column 'deviceId' not found in data stream response. Available columns: results.0.id, results.0.uid, results.0.organizationId, results.0.locationId, results.0.nodeClass, results.0.nodeRoleId, results.0.rolePolicyId, results.0.approvalStatus, results.0.offline, results.0.systemName, results.0.dnsName, results.0.created, results.0.lastContact, results.0.lastUpdate, results.0.deviceId, results.0.displayName, results.1.id, results.1.uid, results.1.organizationId, results.1.locationId, results.1.nodeClass, results.1.nodeRoleId, results.1.rolePolicyId, results.1.approvalStatus, results.1.offline, results.1.systemName, results.1.dnsName, results.1.created, results.1.lastContact, results.1.lastUpdate, results.1.deviceId, results.1.displayName, metadata.next_page_token", "stack": "Error: ID column 'deviceId' not found in data stream response. Available columns: results.0.id, results.0.uid, results.0.organizationId, results.0.locationId, results.0.nodeClass, results.0.nodeRoleId, results.0.rolePolicyId, results.0.approvalStatus, results.0.offline, results.0.systemName, results.0.dnsName, results.0.created, results.0.lastContact, results.0.lastUpdate, results.0.deviceId, results.0.displayName, results.1.id, results.1.uid, results.1.organizationId, results.1.locationId, results.1.nodeClass, results.1.nodeRoleId, results.1.rolePolicyId, results.1.approvalStatus, results.1.offline, results.1.systemName, results.1.dnsName, results.1.created, results.1.lastContact, results.1.lastUpdate, results.1.deviceId, results.1.displayName, metadata.next_page_token at dUe (/src/stepExecutor/mapStreamDataToVertices.ts:62:15) at E1r (/src/stepExecutor/handler.ts:172:33) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async <anonymous> (/src/createMessageHandler.ts:74:13) at async Promise.all (index 0) at async <anonymous> (/src/createMessageHandler.ts:121:32) at async Hcr (/node_modules/.pnpm/@middy+core@2.5.7/node_modules/@middy/core/index.js:86:26)" }

I broke this during my testing adding more devices, pushing a fix for this now

@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 9, 2026

I'm getting this error on imports all of a sudden @Deenk - specifically importing devices:
{ "name": "Error", "message": "ID column 'deviceId' not found in data stream response. Available columns: results.0.id, results.0.uid, results.0.organizationId, results.0.locationId, results.0.nodeClass, results.0.nodeRoleId, results.0.rolePolicyId, results.0.approvalStatus, results.0.offline, results.0.systemName, results.0.dnsName, results.0.created, results.0.lastContact, results.0.lastUpdate, results.0.deviceId, results.0.displayName, results.1.id, results.1.uid, results.1.organizationId, results.1.locationId, results.1.nodeClass, results.1.nodeRoleId, results.1.rolePolicyId, results.1.approvalStatus, results.1.offline, results.1.systemName, results.1.dnsName, results.1.created, results.1.lastContact, results.1.lastUpdate, results.1.deviceId, results.1.displayName, metadata.next_page_token", "stack": "Error: ID column 'deviceId' not found in data stream response. Available columns: results.0.id, results.0.uid, results.0.organizationId, results.0.locationId, results.0.nodeClass, results.0.nodeRoleId, results.0.rolePolicyId, results.0.approvalStatus, results.0.offline, results.0.systemName, results.0.dnsName, results.0.created, results.0.lastContact, results.0.lastUpdate, results.0.deviceId, results.0.displayName, results.1.id, results.1.uid, results.1.organizationId, results.1.locationId, results.1.nodeClass, results.1.nodeRoleId, results.1.rolePolicyId, results.1.approvalStatus, results.1.offline, results.1.systemName, results.1.dnsName, results.1.created, results.1.lastContact, results.1.lastUpdate, results.1.deviceId, results.1.displayName, metadata.next_page_token at dUe (/src/stepExecutor/mapStreamDataToVertices.ts:62:15) at E1r (/src/stepExecutor/handler.ts:172:33) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async <anonymous> (/src/createMessageHandler.ts:74:13) at async Promise.all (index 0) at async <anonymous> (/src/createMessageHandler.ts:121:32) at async Hcr (/node_modules/.pnpm/@middy+core@2.5.7/node_modules/@middy/core/index.js:86:26)" }

I broke this during my testing adding more devices, pushing a fix for this now

@gdorward this is now fixed

"name": "name",
"type": { "value": "NinjaOne Organization" },
"properties": [{ "organizationId": "id" }, "description", "nodeApprovalMode"],
"externalLink": "{{instanceUrl}}/app/organizations/{{id}}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be under properties?

Notably, I think you should be able to set links, but we might have an issue with the syntax as there's an open ticket: https://squaredup-eng.atlassian.net/browse/SAAS-9111?search_id=186f205d-1b37-4db8-801f-bc66ebaf628f

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@Deenk feedback on the setup screen:

  • Do we need to ask both the Region and API Region? Would one suffice to derive the API endpoint?
  • Client Secret: lowercase secret in field title, placeholder and tooltip

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@Deenk feedback on Objects

  • My recommendation is to use a Source Type name that reflects how it's called in NinjaOne / API. For instance, in Pingdom, the SourceType is probes / checks not Pingdom Probes. It does not look best but we have a mechanism that lets us define custom friendly name if we wanted to. In short, I'd recommend not artificially adding NinjaOne.... Akin to HaloPSA, we have Agent, not HaloPSA Agent

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@Deenk feedback on OOB dashboards:

  • I recommend setting a fixed timeframe for some of the Overview dashboard so they don't try to load too much / everything. First time users might find that slow. For instance, the Activity Log dashboard times out OOB for me. I'd recommend either setting a timeframe (7 days is often a good middle ground) or filtering to a specific Status (Open)
  • It would be useful on the Device Overview to have at tile towards the top with a drilldown link to the Device perspective, gives a sense of endless exploration out-of-the-box
  • Same with Orgnization Overview
  • Check to see if other Overview dashboards have a relevant drilldown to add too
  • TicketBoards folder -> Ticket Boards

@gdorward
Copy link
Copy Markdown

@Deenk Theres around 19 Data streams that don't support any timeframes but 'None' is not available in the dropdown. This is causing a lot of warnings across multiple OOBs as well as they are defaulting to 'None' image

I think this goes beyond just the OOB dashboards. I set up an Alerts tile and it warns based on the dashboard timeframe, applies None which isn't an option, so a bit confusing. It seems like no timeframes should be listed if they can't be supported
Screenshot 2026-04-14 at 13 50 47
Screenshot 2026-04-14 at 13 51 07

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@Deenk data streams list

  • Devices, Location Devices and Organization Devices: those data streams look similar. If that is from one endpoint with query / URL filters, could they be merged into one data stream with Parameters to filter? Else could they be named to start with Devices (...
  • Same as above with Locations and Organization Locations
  • Software (Global) and Volume (Global) don't return anything. I see they have different API endpoints, so probably makes sense to have them separate to Software or Volume like you have done.
  • Should these data streams be named to be grouped together, e.g. Devices(...: Disks, Volumes, Network Interfaces, Processors, Operating Systems
  • Could Device Health and Health be merged? Same endpoint, different URL query filters it seems

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@Deenk Activities data stream constantly times out for me. I'll pause my review here and wait for @AliceG-Sq 's test comments and @gdorward about timeframe to be addressed. Then I'll do some final checks on column data types.

@vinbab vinbab removed their assignment Apr 14, 2026
@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 23, 2026

@Deenk data streams list

  • Devices, Location Devices and Organization Devices: those data streams look similar. If that is from one endpoint with query / URL filters, could they be merged into one data stream with Parameters to filter? Else could they be named to start with Devices (...
  • Same as above with Locations and Organization Locations
  • Software (Global) and Volume (Global) don't return anything. I see they have different API endpoints, so probably makes sense to have them separate to Software or Volume like you have done.
  • Should these data streams be named to be grouped together, e.g. Devices(...: Disks, Volumes, Network Interfaces, Processors, Operating Systems
  • Could Device Health and Health be merged? Same endpoint, different URL query filters it seems

Thanks for the feedback! I've now addressed all of these. I've changed the names of the data streams as suggested and also merged some where possible so we aren't duplicating data streams

@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 23, 2026

@Deenk feedback on OOB dashboards:

  • I recommend setting a fixed timeframe for some of the Overview dashboard so they don't try to load too much / everything. First time users might find that slow. For instance, the Activity Log dashboard times out OOB for me. I'd recommend either setting a timeframe (7 days is often a good middle ground) or filtering to a specific Status (Open)
  • It would be useful on the Device Overview to have at tile towards the top with a drilldown link to the Device perspective, gives a sense of endless exploration out-of-the-box
  • Same with Orgnization Overview
  • Check to see if other Overview dashboards have a relevant drilldown to add too
  • TicketBoards folder -> Ticket Boards

I've updated the OOB dashboards to reflect all of this feedback. Please take a look when possible!

@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 23, 2026

@Deenk Theres around 19 Data streams that don't support any timeframes but 'None' is not available in the dropdown. This is causing a lot of warnings across multiple OOBs as well as they are defaulting to 'None' image

I think this goes beyond just the OOB dashboards. I set up an Alerts tile and it warns based on the dashboard timeframe, applies None which isn't an option, so a bit confusing. It seems like no timeframes should be listed if they can't be supported Screenshot 2026-04-14 at 13 50 47 Screenshot 2026-04-14 at 13 51 07

@gdorward this should now be fixed. I've actually added timeframe support for a lot of these datatsreams, and fixed the timeframe in the datastreams that do not support timeframes. I was setting timeframe to 'none' when i should have been setting them to 'false' in a lot of cases

@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 23, 2026

@Deenk feedback on Objects

  • My recommendation is to use a Source Type name that reflects how it's called in NinjaOne / API. For instance, in Pingdom, the SourceType is probes / checks not Pingdom Probes. It does not look best but we have a mechanism that lets us define custom friendly name if we wanted to. In short, I'd recommend not artificially adding NinjaOne.... Akin to HaloPSA, we have Agent, not HaloPSA Agent

Thanks, I have now removed the NinjaOne prefix from objects to address this

@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 23, 2026

@Deenk feedback on the setup screen:

  • Do we need to ask both the Region and API Region? Would one suffice to derive the API endpoint?
  • Client Secret: lowercase secret in field title, placeholder and tooltip

Thanks @vinbab , although LCP does not support deriving URLs like this (from what I can see), I have managed to discover you can get a token from the base url, so I now only need to use one radio selector. I've made the change.

Secret should now also be uppercase in the locations you mentioned

@Deenk
Copy link
Copy Markdown
Author

Deenk commented Apr 23, 2026

@Deenk Activities data stream constantly times out for me. I'll pause my review here and wait for @AliceG-Sq 's test comments and @gdorward about timeframe to be addressed. Then I'll do some final checks on column data types.

@vinbab fixed this now, paging was partially broken for this data stream

@github-actions
Copy link
Copy Markdown

🧩 Plugin PR Summary

📦 Modified Plugins

  • plugins/NinjaOne/v1

📋 Results

Step Status
Validation ✅ Passed
Deployment 🚀 Deployed

🔍 Validation Details

ninja-one
{
  "valid": true,
  "pluginName": "ninja-one",
  "pluginType": "cloud",
  "summary": {
    "Data Streams": 37,
    "Import Definitions": 1,
    "UI Configuration": true,
    "Has Icon": true,
    "Has Default Content": true,
    "Config Validation": false,
    "Custom Types": true
  }
}

"h": 2,
"x": 0,
"y": 0,
"i": "sec-kpi-activethreats-001",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

},
{
"category": "feedback",
"url": "https://feedback.squaredup.com/plugins/p/ninjaone",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"mode": "none"
},
"expandInnerObjects": true,
"endpointPath": "/v2/organization/{{objects[0].organizationId}}/locations",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the expression you're specifying objects[0] meaning it will only take the first selected org. If you want this to work with multiple, you'll either need to:

  • Use the 'request per object' datastream so a request is made for each object in scope
  • Use an API endpoint that access multiple organisation IDs and then map over objects appropriately.

"h": 4,
"x": 0,
"y": 2,
"i": "backup-bar-usage-001",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These should all be guids I think?

"name": "name",
"type": { "value": "NinjaOne Organization" },
"properties": [{ "organizationId": "id" }, "description", "nodeApprovalMode"],
"externalLink": "{{instanceUrl}}/app/organizations/{{id}}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new-plugin Used to PR newly added plugins

Development

Successfully merging this pull request may close these issues.

5 participants