-
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(freshdesk): add Freshdesk users integration (#63)
## Describe your changes Add the following to Freshdesk integration https://www.loom.com/share/b6d2bf3ce1c7430780a7094fd739a6a6?sid=4067bdca-418c-4979-b224-e49165f42891 ### Syncs - users ### Actions - create-user - delete-user **NOTE**: (Watch the video for the report!) The freshdesk users sync pagination system is not working. I have two hypothesis: 1. There might be a bug in nango's [*link async generator function](https://github.com/NangoHQ/nango/blob/f3743a2104030f5737f9916125dcc1873f226440/packages/shared/lib/services/paginate.service.ts#L79C5-L116C6). Also, I'm not sure how the current could possibly update the `page` query parameter the next page number is extracted from the link based on the following proxy configuration ``` const proxyConfiguration: ProxyConfiguration = { endpoint: '/api/v2/contacts', retries: 10, paginate: { // where does the "page" query parameter go? type: 'link', limit_name_in_request: 'per_page', link_rel_in_response_header: 'next', limit: 1 } }; ``` OR 2. Freshdesk list of contacts API response is not truly providing > The 'link' header in the response will hold the next page url if exists. If you have reached the last page of objects, then the link header will not be set. https://developer.freshdesk.com/api/#pagination ## Issue ticket number and link ## 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> Co-authored-by: Khaliq <khaliq@nango.dev>
- Loading branch information
1 parent
6f59edc
commit 1be625c
Showing
41 changed files
with
1,305 additions
and
35 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,48 @@ | ||
import type { NangoAction, ProxyConfiguration, Contact, CreateContact } from '../../models'; | ||
import { createContactSchema } from '../schema.zod.js'; | ||
import type { FreshdeskContact } from '../types'; | ||
import { toContact } from '../mappers/to-contact.js'; | ||
|
||
/** | ||
* Creates a new user in Freshdesk by validating input data against a schema, | ||
* sending a request to the Freshdesk API, logging any validation errors, and | ||
* returning a common Nango Contact object | ||
* | ||
* Create user Freshdesk API docs: https://developer.freshdesk.com/api/#create_contact | ||
* | ||
*/ | ||
export default async function runAction(nango: NangoAction, input: CreateContact): Promise<Contact> { | ||
const parsedInput = createContactSchema.safeParse(input); | ||
|
||
if (!parsedInput.success) { | ||
for (const error of parsedInput.error.errors) { | ||
await nango.log(`Invalid input provided to create a contact: ${error.message} at path ${error.path.join('.')}`, { level: 'error' }); | ||
} | ||
|
||
throw new nango.ActionError({ | ||
message: 'Invalid input provided to create a contact' | ||
}); | ||
} | ||
|
||
const { email, phone, mobile } = parsedInput.data; | ||
|
||
if (!email && !phone && !mobile) { | ||
await nango.log('At least one of email, phone, or mobile must be provided.', { level: 'error' }); | ||
|
||
throw new nango.ActionError({ | ||
message: 'At least one of email, phone, or mobile must be provided.' | ||
}); | ||
} | ||
|
||
const config: ProxyConfiguration = { | ||
endpoint: `/api/v2/contacts`, | ||
data: parsedInput.data, | ||
retries: 10 | ||
}; | ||
|
||
const response = await nango.post<FreshdeskContact>(config); | ||
|
||
const { data: freshdeskContact } = response; | ||
|
||
return toContact(freshdeskContact); | ||
} |
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,76 @@ | ||
import type { NangoAction, ProxyConfiguration, User, FreshdeskCreateUser } from '../../models'; | ||
import { freshdeskCreateUserSchema } from '../schema.zod.js'; | ||
import type { FreshdeskAgent } from '../types'; | ||
|
||
export default async function runAction(nango: NangoAction, input: FreshdeskCreateUser): Promise<User> { | ||
const parsedInput = freshdeskCreateUserSchema.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; | ||
|
||
userInput.ticket_scope = categorizeTicketScope(userInput.ticketScope || 'globalAccess'); | ||
|
||
if (userInput.agentType) { | ||
userInput.agent_type = categorizeAgentType(userInput.agentType); | ||
} | ||
|
||
const config: ProxyConfiguration = { | ||
// https://developer.freshdesk.com/api/#create_agent | ||
endpoint: `/api/v2/agents`, | ||
data: { | ||
...userInput, | ||
name: `${firstName} ${lastName}` | ||
}, | ||
retries: 10 | ||
}; | ||
|
||
const response = await nango.post<FreshdeskAgent>(config); | ||
|
||
const { data } = response; | ||
|
||
const [firstNameOutput, lastNameOutput] = (data?.contact?.name ?? '').split(' '); | ||
|
||
const user: User = { | ||
id: data.id.toString(), | ||
firstName: firstNameOutput || firstName, | ||
lastName: lastNameOutput || lastName, | ||
email: data.contact.email || '' | ||
}; | ||
|
||
return user; | ||
} | ||
|
||
function categorizeTicketScope(ticketScope: string): number { | ||
switch (ticketScope) { | ||
case 'globalAccess': | ||
return 1; | ||
case 'groupAccess': | ||
return 2; | ||
case 'restrictedAccess': | ||
return 3; | ||
default: | ||
return 1; | ||
} | ||
} | ||
|
||
function categorizeAgentType(agentType: string): number { | ||
switch (agentType) { | ||
case 'support': | ||
return 1; | ||
case 'field': | ||
return 2; | ||
case 'collaborator': | ||
return 3; | ||
default: | ||
return 1; | ||
} | ||
} |
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,33 @@ | ||
import type { NangoAction, ProxyConfiguration, SuccessResponse, IdEntity } from '../../models'; | ||
import { idEntitySchema } from '../schema.zod.js'; | ||
|
||
/** | ||
* Deletes a user in Freshdesk | ||
* | ||
* Delete user Freshdesk API docs: https://developer.freshdesk.com/api/#soft_delete_contact | ||
* | ||
*/ | ||
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 = { | ||
endpoint: `/api/v2/contacts/${parsedInput.data.id}`, | ||
retries: 10 | ||
}; | ||
|
||
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,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: 'Invalid input provided to delete a user' | ||
}); | ||
} | ||
|
||
const config: ProxyConfiguration = { | ||
// https://developer.freshdesk.com/api/#delete_agent | ||
endpoint: `/api/v2/agents/${parsedInput.data.id}`, | ||
retries: 10 | ||
}; | ||
|
||
await nango.delete(config); | ||
|
||
return { | ||
success: true | ||
}; | ||
} |
4 changes: 4 additions & 0 deletions
4
integrations/freshdesk/fixtures/create-contact-iteration.json
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,4 @@ | ||
{ | ||
"email": "john0${iteration}@doe.com", | ||
"name": "John Doe" | ||
} |
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,4 @@ | ||
{ | ||
"email": "john@doe.com", | ||
"name": "John Doe" | ||
} |
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,5 @@ | ||
{ | ||
"email": "john@doe.com", | ||
"firstName": "John", | ||
"lastName": "Doe" | ||
} |
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,3 @@ | ||
{ | ||
"id": "501001204038" | ||
} |
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 { FreshdeskContact } from '../types'; | ||
import type { Contact } from '../../models'; | ||
|
||
export function toContact(contact: FreshdeskContact): Contact { | ||
return { | ||
id: contact.id.toString(), | ||
name: contact.name, | ||
active: contact.active, | ||
email: contact.email, | ||
companyId: contact.company_id?.toString(), | ||
jobTitle: contact.job_title, | ||
phone: contact.phone, | ||
mobile: contact.mobile, | ||
createdAt: contact.created_at, | ||
updatedAt: contact.updated_at | ||
}; | ||
} |
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 @@ | ||
[] |
13 changes: 13 additions & 0 deletions
13
integrations/freshdesk/mocks/contacts/Contact/batchSave.json
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,13 @@ | ||
[ | ||
{ | ||
"id": "501001204038", | ||
"name": "John Doe", | ||
"active": false, | ||
"email": "john1@doe.com", | ||
"jobTitle": null, | ||
"phone": null, | ||
"mobile": null, | ||
"createdAt": "2024-10-21T09:26:53Z", | ||
"updatedAt": "2024-10-21T09:29:51Z" | ||
} | ||
] |
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,5 @@ | ||
{ | ||
"email": "john@doe.com", | ||
"firstName": "John", | ||
"lastName": "Doe" | ||
} |
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,6 @@ | ||
{ | ||
"id": "501001204047", | ||
"firstName": "John", | ||
"lastName": "Doe", | ||
"email": "john@doe.com" | ||
} |
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,3 @@ | ||
{ | ||
"id": "501001204038" | ||
} |
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,3 @@ | ||
{ | ||
"success": true | ||
} |
Oops, something went wrong.