Skip to content

Commit

Permalink
feat(Intercom): add users syncs and actions to intercom integration (#64
Browse files Browse the repository at this point in the history
)

## Describe your changes
https://www.loom.com/share/4766f2b5bb5d4fa286126b7c5cf34469

Add the following endpoints to the Intercom integration:"

### Syncs
- users

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

## Issue ticket number and link
N/A

## 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)
- [X] 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 21, 2024
1 parent f9b5a6d commit 7d7b609
Show file tree
Hide file tree
Showing 23 changed files with 1,265 additions and 0 deletions.
39 changes: 39 additions & 0 deletions flows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3605,6 +3605,16 @@ integrations:
input: IdEntity
endpoint: GET /single-article
output: Article
create-user:
description: Creates a user in Intercom
input: IntercomCreateUser
endpoint: POST /users
output: User
delete-user:
description: Deletes a user in Intercom
endpoint: DELETE /users
output: SuccessResponse
input: IdEntity
syncs:
conversations:
runs: every 6 hours
Expand Down Expand Up @@ -3636,9 +3646,18 @@ integrations:
track_deletes: true
version: 1.0.0
endpoint: GET /articles
users:
runs: every 6 hours
description: |
Fetches a list of users from Intercom
output: User
sync_type: incremental
endpoint: GET /users
models:
IdEntity:
id: string
SuccessResponse:
success: boolean
Contact:
id: string
workspace_id: string
Expand Down Expand Up @@ -3741,6 +3760,26 @@ integrations:
pt-BR: ArticleContent | null
zh-CN: ArticleContent | null
zh-TW: ArticleContent | null
User:
id: string
email: string
firstName: string
lastName: string
CreateUser:
firstName: string
lastName: string
email: string
IntercomCreateUser:
firstName: string
lastName: string
email: string
external_id?: string
phone?: string
avatar?: string
signed_up_at?: number
last_seen_at?: number
owner_id?: string
unsubscribed_from_emails?: boolean
jira:
syncs:
issues:
Expand Down
52 changes: 52 additions & 0 deletions integrations/intercom/actions/create-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { NangoAction, ProxyConfiguration, User, IntercomCreateUser } from '../../models';
import { toUser } from '../mappers/to-user.js';
import { intercomCreateUserSchema } from '../schema.zod.js';
import type { IntercomContact } from '../types';

/**
* Creates an Intercom user contact.
*
* This function validates the input against the defined schema and constructs a request
* to the Intercom API to create a new user contact. 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 {IntercomCreateUser} 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://developers.intercom.com/docs/references/rest-api/api.intercom.io/contacts/createcontact
*/
export default async function runAction(nango: NangoAction, input: IntercomCreateUser): Promise<User> {
const parsedInput = intercomCreateUserSchema.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, ...userInput } = parsedInput.data;

const config: ProxyConfiguration = {
// https://developers.intercom.com/docs/references/rest-api/api.intercom.io/contacts/createcontact
endpoint: `/contacts`,
data: {
...userInput,
role: 'user',
name: `${firstName} ${lastName}`
},
retries: 10
};

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

return toUser(response.data);
}
46 changes: 46 additions & 0 deletions integrations/intercom/actions/delete-user.ts
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';
import type { IntercomDeleteContactResponse } from '../types';

/**
* Deletes an Intercom user contact.
*
* This function validates the input against the defined schema and constructs a request
* to the Intercom API to delete a user contact 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://developers.intercom.com/docs/references/rest-api/api.intercom.io/contacts/deletecontact
*/
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://developers.intercom.com/docs/references/rest-api/api.intercom.io/contacts/deletecontact
endpoint: `/contacts/${parsedInput.data.id}`,
retries: 10
};

const response = await nango.delete<IntercomDeleteContactResponse>(config);

return {
success: response.data.deleted
};
}
5 changes: 5 additions & 0 deletions integrations/intercom/fixtures/create-user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"email": "john@doe.com",
"firstName": "John",
"lastName": "Doe"
}
3 changes: 3 additions & 0 deletions integrations/intercom/fixtures/delete-user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"id": "671683207d3fbeb337586673"
}
19 changes: 19 additions & 0 deletions integrations/intercom/mappers/to-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { User } from '../../models';
import type { IntercomContact } from '../types';

/**
* Maps an Intercom API contact object to a Nango User object.
*
* @param contact The raw contact object from the Intercom API.
* @returns Mapped User object with essential properties.
*/
export function toUser(contact: IntercomContact): User {
const [firstName = '', lastName = ''] = (contact?.name ?? '').split(' ');

return {
id: contact.id,
email: contact.email,
firstName,
lastName
};
}
5 changes: 5 additions & 0 deletions integrations/intercom/mocks/create-user/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"email": "john@doe.com",
"firstName": "John",
"lastName": "Doe"
}
6 changes: 6 additions & 0 deletions integrations/intercom/mocks/create-user/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"id": "671683207d3fbeb337586673",
"email": "john@doe.com",
"firstName": "John",
"lastName": "Doe"
}
3 changes: 3 additions & 0 deletions integrations/intercom/mocks/delete-user/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"id": "671683207d3fbeb337586673"
}
3 changes: 3 additions & 0 deletions integrations/intercom/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,6 @@
{
"id": "671683207d3fbeb337586673",
"external_id": null,
"type": "contact",
"deleted": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"type": "contact",
"id": "671683207d3fbeb337586673",
"workspace_id": "s0lour9i",
"external_id": null,
"role": "user",
"email": "john@doe.com",
"phone": null,
"name": "John Doe",
"avatar": null,
"owner_id": null,
"social_profiles": {
"type": "list",
"data": []
},
"has_hard_bounced": false,
"marked_email_as_spam": false,
"unsubscribed_from_emails": false,
"created_at": 1729528609,
"updated_at": 1729528609,
"signed_up_at": null,
"last_seen_at": null,
"last_replied_at": null,
"last_contacted_at": null,
"last_email_opened_at": null,
"last_email_clicked_at": null,
"language_override": null,
"browser": null,
"browser_version": null,
"browser_language": null,
"os": null,
"location": {
"type": "location",
"country": null,
"region": null,
"city": null,
"country_code": null,
"continent_code": null
},
"android_app_name": null,
"android_app_version": null,
"android_device": null,
"android_os_version": null,
"android_sdk_version": null,
"android_last_seen_at": null,
"ios_app_name": null,
"ios_app_version": null,
"ios_device": null,
"ios_os_version": null,
"ios_sdk_version": null,
"ios_last_seen_at": null,
"custom_attributes": {},
"tags": {
"type": "list",
"data": [],
"url": "/contacts/671683207d3fbeb337586673/tags",
"total_count": 0,
"has_more": false
},
"notes": {
"type": "list",
"data": [],
"url": "/contacts/671683207d3fbeb337586673/notes",
"total_count": 0,
"has_more": false
},
"companies": {
"type": "list",
"data": [],
"url": "/contacts/671683207d3fbeb337586673/companies",
"total_count": 0,
"has_more": false
},
"opted_out_subscription_types": {
"type": "list",
"data": [],
"url": "/contacts/671683207d3fbeb337586673/subscriptions",
"total_count": 0,
"has_more": false
},
"opted_in_subscription_types": {
"type": "list",
"data": [],
"url": "/contacts/671683207d3fbeb337586673/subscriptions",
"total_count": 0,
"has_more": false
},
"utm_campaign": null,
"utm_content": null,
"utm_medium": null,
"utm_source": null,
"utm_term": null,
"referrer": null,
"sms_consent": false,
"unsubscribed_from_sms": false
}
Loading

0 comments on commit 7d7b609

Please # to comment.