Skip to content
This repository has been archived by the owner on Sep 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #11 from JupiterOne/INT-11232
Browse files Browse the repository at this point in the history
Update and fix pagination
  • Loading branch information
VDubber authored Jul 2, 2024
2 parents f8f06d9 + 1e77f5f commit 2678b3e
Show file tree
Hide file tree
Showing 11 changed files with 4,494 additions and 1,680 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
name: Setup Node
uses: actions/setup-node@v1
with:
node-version: 14.x
node-version: 18.x

- name: Check out code repository source code
uses: actions/checkout@v2
Expand All @@ -37,7 +37,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: 14.x
node-version: 18.x

- name: Check out repo
uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/questions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
name: Setup Node
uses: actions/setup-node@v1
with:
node-version: 14.x
node-version: 18.x

- name: Check out `main` branch
uses: actions/checkout@v2
Expand Down
2 changes: 2 additions & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
18

12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ and this project adheres to

## [Unreleased]

## 1.1.1 - 2024-07-02

### Fixed

- Pagination was incorrectly adding serach params to the URL. Fixed.
- Handle 422s for devices that are not running MacOS and therefore don't support
fetching of users.

### Upgraded

- Upgraded to Nodejs 18 and SDK v12

## 1.1.1 - 2022-06-20

### Added
Expand Down
15 changes: 9 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jupiterone/graph-simplemdm",
"version": "1.1.1",
"version": "1.1.2",
"description": "A JupiterOne Integration for ingesting data of the SimpleMDM API",
"repository": {
"type": "git",
Expand All @@ -16,6 +16,9 @@
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"start": "j1-integration collect",
"graph": "j1-integration visualize",
Expand All @@ -34,16 +37,16 @@
"prepush": "yarn format:check && yarn lint && yarn type-check && jest --changedSince main"
},
"peerDependencies": {
"@jupiterone/integration-sdk-core": "^8.8.0"
"@jupiterone/integration-sdk-core": "^12.8.3"
},
"devDependencies": {
"@babel/preset-typescript": "^7.17.12",
"@jupiterone/integration-sdk-core": "^8.8.0",
"@jupiterone/integration-sdk-dev-tools": "^8.8.0",
"@jupiterone/integration-sdk-testing": "^8.8.0"
"@jupiterone/integration-sdk-core": "^12.8.3",
"@jupiterone/integration-sdk-dev-tools": "^12.8.3",
"@jupiterone/integration-sdk-testing": "^12.8.3"
},
"dependencies": {
"@lifeomic/attempt": "^3.0.2",
"@lifeomic/attempt": "^3.1.0",
"node-fetch": "2"
}
}
54 changes: 40 additions & 14 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@ import {
SimpleMDMUser,
} from './types';

import { URL, URLSearchParams } from 'url';

export type ResourceIteratee<T> = (each: T) => Promise<void> | void;

export class APIClient {
constructor(readonly config: IntegrationConfig) {}

private perPage = 100;
private baseUri = `https://a.simplemdm.com`;
private baseUri = new URL(`https://a.simplemdm.com`);
private encodedApiKey = Buffer.from(this.config.apiKey + ':').toString(
'base64',
);

private withBaseUri = (path: string) => `${this.baseUri}${path}`;
private withBaseUri = (path: string, params?: Record<string, string>) => {
const url = new URL(path, this.baseUri);
url.search = new URLSearchParams(params).toString();
return url.toString();
};

private checkStatus = (response: Response) => {
if (response.ok) {
Expand Down Expand Up @@ -73,6 +78,13 @@ export class APIClient {
}
}

/**
* Pagination logic based on docs found here: https://api.simplemdm.com/#pagination
* @param uri
* @param method
* @param iteratee
* @private
*/
private async paginatedRequest<T>(
uri: string,
method: 'GET' | 'HEAD' = 'GET',
Expand All @@ -85,20 +97,30 @@ export class APIClient {
nextUri || uri,
method,
);
nextUri = response['has_more']
? `${uri}&starting_after=${
response.data[response.data.length - 1].id
}`
: null;

if (response.has_more && response.data.length > 0) {
nextUri = this.withBaseUri(uri, {
limit: '100',
starting_after:
response.data[response.data.length - 1].id.toString(),
});
} else {
nextUri = null;
}

for (const item of response.data) {
await iteratee(item as unknown as T);
}
} while (nextUri);
} catch (err) {
if (err instanceof IntegrationProviderAPIError) {
throw err;
}

throw new IntegrationProviderAPIError({
cause: new Error(err.message),
endpoint: uri,
status: err.statusCode,
status: err.status,
statusText: err.message,
});
}
Expand Down Expand Up @@ -134,7 +156,7 @@ export class APIClient {
iteratee: ResourceIteratee<SimpleMDMDevice>,
): Promise<void> {
await this.paginatedRequest<SimpleMDMDevice>(
this.withBaseUri(`/api/v1/devices?limit=${this.perPage}`),
this.withBaseUri(`/api/v1/devices`, { limit: '100' }),
'GET',
iteratee,
);
Expand All @@ -143,14 +165,17 @@ export class APIClient {
/**
* Iterates each device's installed application in the provider.
*
* @param deviceId
* @param iteratee receives each resource to produce entities/relationships
*/
public async iterateInstalledApplications(
deviceId: string,
iteratee: ResourceIteratee<any>,
): Promise<void> {
await this.paginatedRequest<any>(
this.withBaseUri(`/api/v1/devices/${deviceId}/installed_apps`),
this.withBaseUri(`/api/v1/devices/${deviceId}/installed_apps`, {
limit: '100',
}),
'GET',
iteratee,
);
Expand All @@ -159,16 +184,17 @@ export class APIClient {
/**
* Iterates each user resource in the provider.
*
* @param deviceId
* @param iteratee receives each resource to produce entities/relationships
*/
public async iterateUsers(
deviceId: string,
iteratee: ResourceIteratee<SimpleMDMUser>,
): Promise<void> {
await this.paginatedRequest<SimpleMDMUser>(
this.withBaseUri(
`/api/v1/devices/${deviceId}/users?limit=${this.perPage}`,
),
this.withBaseUri(`/api/v1/devices/${deviceId}/users`, {
limit: '100',
}),
'GET',
iteratee,
);
Expand Down
Loading

0 comments on commit 2678b3e

Please sign in to comment.