Skip to content
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

feat(zapier-scim): ext 290/zapier user provisioning #200

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions flows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15406,6 +15406,50 @@ integrations:
trackingCategoryId: string
trackingOptionId: string
options: string[]
zapier-scim:
actions:
create-user:
description: Creates a user in Zapier
output: User
endpoint:
method: POST
path: /users
group: Users
input: CreateUser
disable-user:
description: Disables a user in Zapier
endpoint:
method: DELETE
path: /users
group: Users
output: SuccessResponse
input: IdEntity
syncs:
users:
description: |
Fetches the list of users from Zapier
endpoint:
method: GET
path: /users
group: 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
zendesk:
syncs:
tickets:
Expand Down
51 changes: 51 additions & 0 deletions integrations/zapier-scim/actions/create-user.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!-- BEGIN GENERATED CONTENT -->
# Create User

## General Information

- **Description:** Creates a user in Zapier
- **Version:** 0.0.1
- **Group:** Users
- **Scopes:** _None_
- **Endpoint Type:** Action
- **Code:** [github.com](https://github.com/NangoHQ/integration-templates/tree/main/integrations/zapier-scim/actions/create-user.ts)


## Endpoint Reference

### Request Endpoint

`POST /users`

### Request Query Parameters

_No request parameters_

### Request Body

```json
{
"firstName": "<string>",
"lastName": "<string>",
"email": "<string>"
}
```

### Request Response

```json
{
"id": "<string>",
"email": "<string>",
"firstName": "<string>",
"lastName": "<string>"
}
```

## Changelog

- [Script History](https://github.com/NangoHQ/integration-templates/commits/main/integrations/zapier-scim/actions/create-user.ts)
- [Documentation History](https://github.com/NangoHQ/integration-templates/commits/main/integrations/zapier-scim/actions/create-user.md)

<!-- END GENERATED CONTENT -->

54 changes: 54 additions & 0 deletions integrations/zapier-scim/actions/create-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { NangoAction, ProxyConfiguration, User, CreateUser } from '../../models';
import type { ScimUser } from '../types';
import { createUserSchema } from '../schema.zod.js';

/**
* Executes the create user action by validating input, constructing the request configuration,
* and making the Zapier SCIM API call to create a new user.
*/
export default async function runAction(nango: NangoAction, input: CreateUser): Promise<User> {
const parsedInput = createUserSchema.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 config: ProxyConfiguration = {
// https://help.zapier.com/hc/en-us/articles/8496291497741-Provision-user-accounts-with-SCIM#h_01HE8NPZMWDB3JG39AKV820GCX
endpoint: 'scim/v2/Users',
data: {
schemas: ['urn:ietf:params:scim:schemas:core:2.0:User'],
name: {
givenName: parsedInput.data.firstName,
familyName: parsedInput.data.lastName
},
userName: parsedInput.data.email,
emails: [
{
primary: true,
value: parsedInput.data.email
}
]
},
retries: 10
};

const response = await nango.post<ScimUser>(config);

const newUser = response.data;

const user: User = {
id: newUser?.id ? newUser.id.toString() : '',
firstName: newUser?.name?.givenName || '',
lastName: newUser?.name?.familyName || '',
email: parsedInput.data.email
};

return user;
}
46 changes: 46 additions & 0 deletions integrations/zapier-scim/actions/disable-user.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!-- BEGIN GENERATED CONTENT -->
# Disable User

## General Information

- **Description:** Disables a user in Zapier
- **Version:** 0.0.1
- **Group:** Users
- **Scopes:** _None_
- **Endpoint Type:** Action
- **Code:** [github.com](https://github.com/NangoHQ/integration-templates/tree/main/integrations/zapier-scim/actions/disable-user.ts)


## Endpoint Reference

### Request Endpoint

`DELETE /users`

### Request Query Parameters

_No request parameters_

### Request Body

```json
{
"id": "<string>"
}
```

### Request Response

```json
{
"success": "<boolean>"
}
```

## Changelog

- [Script History](https://github.com/NangoHQ/integration-templates/commits/main/integrations/zapier-scim/actions/disable-user.ts)
- [Documentation History](https://github.com/NangoHQ/integration-templates/commits/main/integrations/zapier-scim/actions/disable-user.md)

<!-- END GENERATED CONTENT -->

28 changes: 28 additions & 0 deletions integrations/zapier-scim/actions/disable-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { NangoAction, ProxyConfiguration, SuccessResponse, IdEntity } from '../../models';
import { idEntitySchema } from '../schema.zod.js';

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: 'Id is required to disable a user'
});
}

const config: ProxyConfiguration = {
// https://help.zapier.com/hc/en-us/articles/8496291497741-Provision-user-accounts-with-SCIM#h_01HE8NPZMWDB3JG39AKV820GCX
endpoint: `scim/v2/Users/${parsedInput.data.id}`,
retries: 10
};

await nango.delete(config);

return {
success: true
};
}
11 changes: 11 additions & 0 deletions integrations/zapier-scim/mappers/to-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { User } from '../../models';
import type { ScimUser } from '../types';

export function toUser(ScimUser: ScimUser): User {
return {
id: ScimUser.id,
email: ScimUser.emails.find((email) => email.primary)?.value ?? '',
firstName: ScimUser.name.givenName ?? '',
lastName: ScimUser.name.familyName ?? ''
};
}
46 changes: 46 additions & 0 deletions integrations/zapier-scim/nango.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
integrations:
zapier-scim:
actions:
create-user:
description: Creates a user in Zapier
output: User
endpoint:
method: POST
path: /users
group: Users
input: CreateUser
disable-user:
description: Disables a user in Zapier
endpoint:
method: DELETE
path: /users
group: Users
output: SuccessResponse
input: IdEntity
syncs:
users:
description: |
Fetches the list of users from Zapier
endpoint:
method: GET
path: /users
group: 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
23 changes: 23 additions & 0 deletions integrations/zapier-scim/schema.zod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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()
});
49 changes: 49 additions & 0 deletions integrations/zapier-scim/syncs/users.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!-- BEGIN GENERATED CONTENT -->
# Users

## General Information

- **Description:** Fetches the list of users from Zapier

- **Version:** 0.0.1
- **Group:** Users
- **Scopes:** _None_
- **Endpoint Type:** Sync
- **Code:** [github.com](https://github.com/NangoHQ/integration-templates/tree/main/integrations/zapier-scim/syncs/users.ts)


## Endpoint Reference

### Request Endpoint

`GET /users`

### Request Query Parameters

- **modified_after:** `(optional, string)` A timestamp (e.g., `2023-05-31T11:46:13.390Z`) used to fetch records modified after this date and time. If not provided, all records are returned. The modified_after parameter is less precise than cursor, as multiple records may share the same modification timestamp.
- **limit:** `(optional, integer)` The maximum number of records to return per page. Defaults to 100.
- **cursor:** `(optional, string)` A marker used to fetch records modified after a specific point in time.If not provided, all records are returned.Each record includes a cursor value found in _nango_metadata.cursor.Save the cursor from the last record retrieved to track your sync progress.Use the cursor parameter together with the limit parameter to paginate through records.The cursor is more precise than modified_after, as it can differentiate between records with the same modification timestamp.
- **filter:** `(optional, added | updated | deleted)` Filter to only show results that have been added or updated or deleted.

### Request Body

_No request body_

### Request Response

```json
{
"id": "<string>",
"email": "<string>",
"firstName": "<string>",
"lastName": "<string>"
}
```

## Changelog

- [Script History](https://github.com/NangoHQ/integration-templates/commits/main/integrations/zapier-scim/syncs/users.ts)
- [Documentation History](https://github.com/NangoHQ/integration-templates/commits/main/integrations/zapier-scim/syncs/users.md)

<!-- END GENERATED CONTENT -->

26 changes: 26 additions & 0 deletions integrations/zapier-scim/syncs/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { NangoSync, ProxyConfiguration, User } from '../../models';
import { toUser } from '../mappers/to-user.js';
import type { ScimUser } from '../types';

export default async function fetchData(nango: NangoSync): Promise<void> {
const config: ProxyConfiguration = {
// https://help.zapier.com/hc/en-us/articles/8496291497741-Provision-user-accounts-with-SCIM#h_01HE8NPZMWDB3JG39AKV820GCX
endpoint: '/Users',
paginate: {
type: 'offset',
offset_name_in_request: 'startIndex',
offset_start_value: 1,
offset_calculation_method: 'per-page',
limit_name_in_request: 'count',
response_path: 'Resources',
limit: 100
},
retries: 10
};

for await (const ScimUsers of nango.paginate<ScimUser>(config)) {
const users: User[] = ScimUsers.map(toUser);

await nango.batchSave<User>(users, 'User');
}
}
Loading
Loading