-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(perimeter81): Add users endpoints to Perimeter81 integration (#81)
## Describe your changes Add Perimeter81 integration with the following endpoints: ### Syncs - users ### Actions - create-user - delete-user **Important Note: No fixtures, nor tests/mocks were generated. I could not test any of the syncs and actions added to the Perimeter81 integration as it requires an enterprise account, which is not accessible to me. Hence, once a connection is setup in Nango, these endpoints need to be tested and validated** ## Issue ticket number and link N/A ## Checklist before requesting a review (skip if just adding/editing APIs & templates) - [ ] I added tests, otherwise the reason is: I could not test any of the syncs and actions added to the Perimeter81 integration as it requires an enterprise account, which is not accessible to me. Hence, once a connection is setup in Nango, these endpoints need to be tested and validated - [X] External API requests have `retries` - [X] Pagination is used where appropriate - [X] The built in `nango.paginate` call is used instead of a `while (true)` loop - [ ] Third party requests are NOT parallelized (this can cause issues with rate limits) - [ ] If a sync requires metadata the `nango.yaml` has `auto_start: false` - [X] If the sync is a `full` sync then `track_deletes: true` is set --------- Co-authored-by: Khaliq <khaliqgant@gmail.com> Co-authored-by: Khaliq <khaliq@nango.dev>
- Loading branch information
1 parent
deff79d
commit 5d68ba9
Showing
8 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import type { NangoAction, ProxyConfiguration, User, Perimeter81CreateUser } from '../../models'; | ||
import { toUser } from '../mappers/to-user.js'; | ||
import { perimeter81CreateUserSchema } from '../schema.zod.js'; | ||
import type { Perimeter81User } from '../types'; | ||
|
||
/** | ||
* Creates an Perimeter81 user. | ||
* | ||
* This function validates the input against the defined schema and constructs a request | ||
* to the Perimeter81 API to create a new user. If the input is invalid, it logs the | ||
* errors and throws an ActionError. | ||
* | ||
* @param {NangoAction} nango - The Nango action context, used for logging and making API requests. | ||
* @param {Perimeter81CreateUser} input - The input data for creating a user contact | ||
* | ||
* @returns {Promise<User>} - A promise that resolves to the created User object. | ||
* | ||
* @throws {nango.ActionError} - Throws an error if the input validation fails. | ||
* | ||
* For detailed endpoint documentation, refer to: | ||
* https://support.perimeter81.com/docs/post-new-member | ||
*/ | ||
export default async function runAction(nango: NangoAction, input: Perimeter81CreateUser): Promise<User> { | ||
const parsedInput = perimeter81CreateUserSchema.safeParse(input); | ||
|
||
if (!parsedInput.success) { | ||
for (const error of parsedInput.error.errors) { | ||
await nango.log(`Invalid input provided to create a user: ${error.message} at path ${error.path.join('.')}`, { level: 'error' }); | ||
} | ||
|
||
throw new nango.ActionError({ | ||
message: 'Invalid input provided to create a user' | ||
}); | ||
} | ||
|
||
const { firstName, lastName, profileData = {}, ...data } = parsedInput.data; | ||
|
||
const config: ProxyConfiguration = { | ||
// https://support.perimeter81.com/docs/post-new-member | ||
endpoint: `/v1/users`, | ||
data: { | ||
...data, | ||
inviteMessage: parsedInput.data.inviteMessage || 'Welcome to the team!', | ||
profileData: { | ||
...profileData, | ||
firstName, | ||
lastName | ||
} | ||
}, | ||
retries: 10 | ||
}; | ||
|
||
const response = await nango.post<Perimeter81User>(config); | ||
|
||
return toUser(response.data); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type { NangoAction, ProxyConfiguration, SuccessResponse, IdEntity } from '../../models'; | ||
import { idEntitySchema } from '../schema.zod.js'; | ||
|
||
/** | ||
* Deletes a Perimeter81 user. | ||
* | ||
* This function validates the input against the defined schema and constructs a request | ||
* to the Perimeter81 API to delete a user by their ID. If the input is invalid, | ||
* it logs the errors and throws an ActionError. | ||
* | ||
* @param {NangoAction} nango - The Nango action context, used for logging and making API requests. | ||
* @param {IdEntity} input - The input data containing the ID of the user contact to be deleted | ||
* | ||
* @returns {Promise<SuccessResponse>} - A promise that resolves to a SuccessResponse object indicating the result of the deletion. | ||
* | ||
* @throws {nango.ActionError} - Throws an error if the input validation fails. | ||
* | ||
* For detailed endpoint documentation, refer to: | ||
* https://support.perimeter81.com/docs/delete-delete-user | ||
*/ | ||
export default async function runAction(nango: NangoAction, input: IdEntity): Promise<SuccessResponse> { | ||
const parsedInput = idEntitySchema.safeParse(input); | ||
|
||
if (!parsedInput.success) { | ||
for (const error of parsedInput.error.errors) { | ||
await nango.log(`Invalid input provided to delete a user: ${error.message} at path ${error.path.join('.')}`, { level: 'error' }); | ||
} | ||
|
||
throw new nango.ActionError({ | ||
message: 'Invalid input provided to delete a user' | ||
}); | ||
} | ||
|
||
const config: ProxyConfiguration = { | ||
// https://support.perimeter81.com/docs/delete-delete-user | ||
endpoint: `/v1/users/${parsedInput.data.id}`, | ||
retries: 10 | ||
}; | ||
|
||
// no body content expected for successful requests | ||
await nango.delete(config); | ||
|
||
return { | ||
success: true | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import type { User } from '../../models'; | ||
import type { Perimeter81User } from '../types'; | ||
|
||
/** | ||
* Maps a Perimeter81 API user object to a Nango User object. | ||
* | ||
* @param perimeter81User The raw contact object from the Perimeter81 API. | ||
* @returns Mapped User object with essential properties. | ||
*/ | ||
export function toUser(perimeter81User: Perimeter81User): User { | ||
return { | ||
id: perimeter81User.id, | ||
email: perimeter81User.email, | ||
firstName: perimeter81User.firstName, | ||
lastName: perimeter81User.lastName | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
integrations: | ||
perimeter81: | ||
actions: | ||
create-user: | ||
description: Creates a user in Perimeter81 | ||
input: Perimeter81CreateUser | ||
endpoint: POST /users | ||
output: User | ||
delete-user: | ||
description: Deletes a user in Perimeter81 | ||
endpoint: DELETE /users | ||
output: SuccessResponse | ||
input: IdEntity | ||
syncs: | ||
users: | ||
description: | | ||
Fetches the list of users from Perimeter81 | ||
endpoint: GET /users | ||
sync_type: full | ||
track_deletes: true | ||
runs: every day | ||
output: User | ||
models: | ||
IdEntity: | ||
id: string | ||
SuccessResponse: | ||
success: boolean | ||
User: | ||
id: string | ||
email: string | ||
firstName: string | ||
lastName: string | ||
CreateUser: | ||
firstName: string | ||
lastName: string | ||
email: string | ||
Perimeter81CreateUser: | ||
__extends: CreateUser | ||
idpType?: string | ||
accessGroups?: string[] | ||
emailVerified?: boolean | ||
inviteMessage?: string | ||
origin?: string | ||
profileData?: | ||
roleName?: string | ||
phone?: string | ||
icon?: string | ||
origin?: string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Generated by ts-to-zod | ||
import { z } from 'zod'; | ||
|
||
export const idEntitySchema = z.object({ | ||
id: z.string() | ||
}); | ||
|
||
export const successResponseSchema = z.object({ | ||
success: z.boolean() | ||
}); | ||
|
||
export const userSchema = z.object({ | ||
id: z.string(), | ||
email: z.string(), | ||
firstName: z.string(), | ||
lastName: z.string() | ||
}); | ||
|
||
export const createUserSchema = z.object({ | ||
firstName: z.string(), | ||
lastName: z.string(), | ||
email: z.string() | ||
}); | ||
|
||
export const perimeter81CreateUserSchema = z.object({ | ||
firstName: z.string(), | ||
lastName: z.string(), | ||
email: z.string(), | ||
idpType: z.string().optional(), | ||
accessGroups: z.array(z.string()).optional(), | ||
emailVerified: z.boolean().optional(), | ||
inviteMessage: z.string().optional(), | ||
origin: z.string().optional(), | ||
profileData: z | ||
.object({ | ||
roleName: z.string().optional(), | ||
phone: z.string().optional(), | ||
icon: z.string().optional(), | ||
origin: z.string().optional() | ||
}) | ||
.optional() | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import type { NangoSync, ProxyConfiguration, User } from '../../models'; | ||
import { toUser } from '../mappers/to-user.js'; | ||
import type { Perimeter81User } from '../types'; | ||
|
||
/** | ||
* Fetches Perimeter81 users, maps them to Nango User objects, | ||
* and saves the processed contacts using NangoSync. | ||
* | ||
* This function handles pagination and ensures that all contacts are fetched, | ||
* transformed, and stored. | ||
* | ||
* For endpoint documentation, refer to: | ||
* https://support.perimeter81.com/docs/get-list-users | ||
* | ||
* @param nango An instance of NangoSync for synchronization tasks. | ||
* @returns Promise that resolves when all users are fetched and saved. | ||
*/ | ||
export default async function fetchData(nango: NangoSync): Promise<void> { | ||
const config: ProxyConfiguration = { | ||
// https://support.perimeter81.com/docs/get-list-users | ||
endpoint: '/v1/users', | ||
paginate: { | ||
type: 'offset', | ||
offset_name_in_request: 'page', | ||
offset_start_value: 1, | ||
offset_calculation_method: 'per-page', | ||
limit_name_in_request: 'limit', | ||
response_path: 'data', | ||
limit: 100 | ||
}, | ||
retries: 10 | ||
}; | ||
|
||
for await (const perimeter81Users of nango.paginate<Perimeter81User>(config)) { | ||
const users = perimeter81Users.map(toUser); | ||
|
||
await nango.batchSave<User>(users, 'User'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export interface Perimeter81User { | ||
terminated: boolean; | ||
email: string; | ||
emailVerified: boolean; | ||
initials: string; | ||
roleName: string; | ||
lastName: string; | ||
firstName: string; | ||
id: string; | ||
} |