Skip to content

Commit

Permalink
feat(docuSign): Add DocuSign and DocuSign-sandbox integration (#52)
Browse files Browse the repository at this point in the history
## Describe your changes
Added Docusign and Docusing Sandbox integrations with the following
syncs and actions

### Syncs
- users

### Actions
- create-user
- delete-user


https://www.loom.com/share/a763f0fe2e474563927610c458f6e316?sid=36114b46-9e04-4586-b2c8-523c194e46c6

## Checklist before requesting a review (skip if just adding/editing
APIs & templates)
- [X] I added tests, otherwise the reason is:
- [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>
  • Loading branch information
arealesramirez and khaliqgant authored Oct 17, 2024
1 parent 6d17af6 commit 691f128
Show file tree
Hide file tree
Showing 36 changed files with 919 additions and 70 deletions.
116 changes: 116 additions & 0 deletions flows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1384,6 +1384,122 @@ integrations:
TopicStatusUpdated:
success: string
result: string
docusign:
syncs:
users:
runs: every day
description: |
Fetches a list of users from DocuSign
output: User
track_deletes: true
sync_type: full
endpoint: GET /users
scopes:
- oauth
- user_read
actions:
create-user:
description: Creates a user in DocuSign
output: User
endpoint: POST /users
input: DocuSignCreateUser
scopes:
- oauth
- user_write
delete-user:
description: Deletes a user in DocuSign
endpoint: DELETE /users
output: SuccessResponse
input: IdEntity
scopes:
- oauth
- user_write
models:
IdEntity:
id: string
SuccessResponse:
success: boolean
User:
id: string
email: string
firstName: string
lastName: string
CreateUser:
firstName: string
lastName: string
email: string
DocuSignCreateUser:
firstName: string
lastName: string
email: string
userName?: string
title?: string
phoneNumber?: string
company?: string
countryCode?: string
activationAccessCode?: string
settings?:
language?: string
timeZone?: string
userStatus?: string
docusign-sandbox:
syncs:
users:
runs: every day
description: |
Fetches a list of users from DocuSign sandbox
output: User
track_deletes: true
sync_type: full
endpoint: GET /users
scopes:
- oauth
- user_read
actions:
create-user:
description: Creates a user in DocuSign sandbox
output: User
endpoint: POST /users
input: DocuSignCreateUser
scopes:
- oauth
- user_write
delete-user:
description: Deletes a user in DocuSign sandbox
endpoint: DELETE /users
output: SuccessResponse
input: IdEntity
scopes:
- oauth
- user_write
models:
IdEntity:
id: string
SuccessResponse:
success: boolean
User:
id: string
email: string
firstName: string
lastName: string
CreateUser:
firstName: string
lastName: string
email: string
DocuSignCreateUser:
firstName: string
lastName: string
email: string
userName?: string
title?: string
phoneNumber?: string
company?: string
countryCode?: string
activationAccessCode?: string
settings?:
language?: string
timeZone?: string
userStatus?: string
evaluagent:
syncs:
users:
Expand Down
1 change: 1 addition & 0 deletions integrations/docusign-sandbox/actions
1 change: 1 addition & 0 deletions integrations/docusign-sandbox/fixtures
1 change: 1 addition & 0 deletions integrations/docusign-sandbox/helpers
61 changes: 61 additions & 0 deletions integrations/docusign-sandbox/nango.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
integrations:
docusign-sandbox:
syncs:
users:
runs: every day
description: |
Fetches a list of users from DocuSign sandbox
output: User
track_deletes: true
sync_type: full
endpoint: GET /users
scopes:
- oauth
- user_read
actions:
create-user:
description: Creates a user in DocuSign sandbox
output: User
endpoint: POST /users
input: DocuSignCreateUser
scopes:
- oauth
- user_write
delete-user:
description: Deletes a user in DocuSign sandbox
endpoint: DELETE /users
output: SuccessResponse
input: IdEntity
scopes:
- oauth
- user_write

models:
# Generic
IdEntity:
id: string
SuccessResponse:
success: boolean
User:
id: string
email: string
firstName: string
lastName: string

# User
CreateUser:
firstName: string
lastName: string
email: string
DocuSignCreateUser:
__extends: CreateUser
userName?: string
title?: string
phoneNumber?: string
company?: string
countryCode?: string
activationAccessCode?: string
settings?:
language?: string
timeZone?: string
userStatus?: string
1 change: 1 addition & 0 deletions integrations/docusign-sandbox/schema.zod.ts
1 change: 1 addition & 0 deletions integrations/docusign-sandbox/syncs
1 change: 1 addition & 0 deletions integrations/docusign-sandbox/types.ts
58 changes: 58 additions & 0 deletions integrations/docusign/actions/create-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { NangoAction, ProxyConfiguration, DocuSignCreateUser, User } from '../../models';
import { getRequestInfo } from '../helpers/get-request-info.js';
import { docuSignCreateUserSchema } from '../schema.zod.js';
import type { DocuSignUser } from '../types';

/**
* Executes the create user action by validating input, constructing the request configuration,
* and making the API call to create a new user.
*/
export default async function runAction(nango: NangoAction, input: DocuSignCreateUser): Promise<User> {
const parsedInput = docuSignCreateUserSchema.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 { baseUri, accountId } = await getRequestInfo(nango);

const newUsers = [
{
...parsedInput.data,
userName: input.userName ?? `${parsedInput.data.firstName.toLowerCase()}${parsedInput.data.lastName.toLowerCase()}`
}
];

const config: ProxyConfiguration = {
// https://developers.docusign.com/docs/esign-rest-api/reference/users/users/create/
baseUrlOverride: baseUri,
endpoint: `/restapi/v2.1/accounts/${accountId}/users`,
data: {
newUsers
},
retries: 10
};

const response = await nango.post<{ newUsers: DocuSignUser[] }>(config);
const {
data: { newUsers: createdUsers }
} = response;

const docuSignUser = createdUsers[0];
const [firstNameExtracted, lastNameExtracted] = (docuSignUser?.userName ?? '').split(' ');

const user: User = {
id: docuSignUser?.userId || '',
firstName: docuSignUser?.firstName || firstNameExtracted || '',
lastName: docuSignUser?.lastName || lastNameExtracted || '',
email: docuSignUser?.email || ''
};

return user;
}
35 changes: 35 additions & 0 deletions integrations/docusign/actions/delete-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { NangoAction, ProxyConfiguration, SuccessResponse, IdEntity } from '../../models';
import { getRequestInfo } from '../helpers/get-request-info.js';
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 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 { baseUri, accountId } = await getRequestInfo(nango);

const config: ProxyConfiguration = {
// https://developers.docusign.com/docs/esign-rest-api/reference/users/users/delete/
baseUrlOverride: baseUri,
endpoint: `/restapi/v2.1/accounts/${accountId}/users`,
data: {
users: [{ userId: parsedInput.data.id }]
},
retries: 10
};

await nango.delete(config);

return {
success: true
};
}
5 changes: 5 additions & 0 deletions integrations/docusign/fixtures/create-user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"firstName": "John",
"lastName": "Doe",
"email": "john@doe.com"
}
3 changes: 3 additions & 0 deletions integrations/docusign/fixtures/delete-user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"id": "aad794b9-8041-44ee-bfdc-60c386abf594"
}
34 changes: 34 additions & 0 deletions integrations/docusign/helpers/get-request-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { NangoAction, NangoSync } from '../../models';
import type { UserInfoResponse, AccountInfo } from '../types';

/**
* Retrieves the base URI and account ID of the default account associated with the
* logged-in user from the DocuSign API.
*/
export async function getRequestInfo(nango: NangoAction | NangoSync): Promise<{ baseUri: string; accountId: string }> {
const rootUrl = nango.providerConfigKey.includes('sandbox') ? 'account-d.docusign.com' : 'account.docusign.com';
const response = await nango.get<UserInfoResponse>({
// https://developers.docusign.com/platform/auth/reference/user-info/
baseUrlOverride: `https://${rootUrl}`,
endpoint: '/oauth/userinfo',
retries: 10
});

if (response.data.accounts.length <= 0) {
throw new nango.ActionError({ message: 'failed to get request info' });
}

// There could be multiple accounts tied to the user. Use the default account.
const defaultAccount = response.data.accounts.find((account: AccountInfo) => account.is_default);

if (!defaultAccount) {
throw new nango.ActionError({ message: 'failed to get DocuSign default account' });
}

await nango.log(`Got default DocuSign account`);

return {
baseUri: defaultAccount.base_uri,
accountId: defaultAccount.account_id
};
}
5 changes: 5 additions & 0 deletions integrations/docusign/mocks/create-user/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"firstName": "John",
"lastName": "Doe",
"email": "john@doe.com"
}
6 changes: 6 additions & 0 deletions integrations/docusign/mocks/create-user/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"id": "aad794b9-8041-44ee-bfdc-60c386abf594",
"firstName": "John",
"lastName": "Doe",
"email": "john@doe.com"
}
3 changes: 3 additions & 0 deletions integrations/docusign/mocks/delete-user/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"id": "aad794b9-8041-44ee-bfdc-60c386abf594"
}
3 changes: 3 additions & 0 deletions integrations/docusign/mocks/delete-user/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"success": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"users": [
{
"userId": "aad794b9-8041-44ee-bfdc-60c386abf594",
"userStatus": "closed",
"uri": "/users/aad794b9-8041-44ee-bfdc-60c386abf594"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"sub": "700a33f0-a4bb-4a7b-a478-00ad1917791d",
"name": "A RealUser",
"given_name": "A",
"family_name": "RealUser",
"created": "2024-10-15T15:09:49.867",
"email": "realramirez8606+nango@gmail.com",
"accounts": [
{
"account_id": "b446da56-e1e5-4717-a5be-6a1bd26d7f1d",
"is_default": true,
"account_name": "Nango",
"base_uri": "https://demo.docusign.net"
}
]
}
Loading

0 comments on commit 691f128

Please sign in to comment.