Skip to content

Commit

Permalink
Added integration files for Lever
Browse files Browse the repository at this point in the history
  • Loading branch information
hassan254-prog committed Jan 22, 2024
1 parent 474a2d7 commit 2b24195
Show file tree
Hide file tree
Showing 20 changed files with 1,327 additions and 0 deletions.
21 changes: 21 additions & 0 deletions docs-v2/integration-templates/lever.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: 'Lever API Integration Template'
sidebarTitle: 'Lever'
---

## Get started with the Lever template

<Card title="How to use integration templates"
href="/integration-templates/overview#how-to-use-integration-templates"
icon="book-open">
Learn how to use integration templates in Nango
</Card>

<Card title="Get the Lever template"
href="https://github.com/NangoHQ/nango/tree/master/integration-templates/lever"
icon="github">
Get the latest version of the Lever integration template from GitHub
</Card>

## Need help with the template?
Please reach out on the [Slack community](https://nango.dev/slack), we are very active there and happy to help!
4 changes: 4 additions & 0 deletions docs-v2/integration-templates/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ As Nango and its community expand, we're looking forward to offering hundreds of
Sync Zendesk tickets and articles
</Card>

<Card title="Lever" href="/integration-templates/lever" icon="briefcase">
Sync Lever opportunities, applications, feedback, interviews, notes and offers for a specific opportunitity. Postings and questions for a specific job posting. Actions to create; a note for a specific opportunitity and an opportunitity.
</Card>

<Card title="Pipedrive" href="/integration-templates/pipedrive" icon="user-tie">
Sync Pipedrive activities, deals, organizations and persons
</Card>
Expand Down
1 change: 1 addition & 0 deletions docs-v2/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"integration-templates/hubspot",
"integration-templates/intercom",
"integration-templates/jira",
"integration-templates/lever",
"integration-templates/linear",
"integration-templates/microsoft-active-directory",
"integration-templates/notion",
Expand Down
63 changes: 63 additions & 0 deletions integration-templates/lever/lever-create-note.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { NangoAction, LeverOpportunityNote } from './models';

interface LeverCreateNoteInput {
opportunityId: string;
perform_as?: string;
note_id?: string;
value: string;
secret?: boolean;
score?: number;
notifyFollowers?: boolean;
createdAt?: number;
}

export default async function runAction(nango: NangoAction, input: LeverCreateNoteInput): Promise<LeverOpportunityNote> {
if (!input.opportunityId) {
throw new nango.ActionError({
message: 'opportunity id is a required field'
});
} else if (!input.value) {
throw new nango.ActionError({
message: 'value of the note is a required field'
});
}

const endpoint = `/v1/opportunities/${input.opportunityId}/notes`;

try {
const postData = {
value: input.value,
secret: input.secret,
score: input.score,
notifyFollowers: input.notifyFollowers,
createdAt: input.createdAt
};

const params = Object.entries({
...(input.perform_as ? { perform_as: input.perform_as } : {}),
...(input.note_id ? { note_id: input.note_id } : {})
})
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');

const urlWithParams = `${endpoint}${params ? `?${params}` : ''}`;

const resp = await nango.post({
endpoint: urlWithParams,
data: postData
});

return {
id: resp.data.data.id,
text: resp.data.data.text,
fields: resp.data.data.fields,
user: resp.data.data.user,
secret: resp.data.data.secret,
completedAt: resp.data.data.completedAt,
createdAt: resp.data.data.createdAt,
deletedAt: resp.data.data.deletedAt
};
} catch (error: any) {
throw new Error(`Error in runAction: ${error.message}`);
}
}
113 changes: 113 additions & 0 deletions integration-templates/lever/lever-create-opportunity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import type { NangoAction, LeverOpportunity } from './models';

interface PhoneEntry {
value?: string;
type?: string;
}
interface ArchievedEntry {
archivedAt?: number;
reason?: string;
}
interface LeverCreateNoteInput {
perform_as: string;
parse?: boolean;
perform_as_posting_owner?: boolean;
name?: string;
headline?: string;
stage?: string;
location?: string;
phones?: PhoneEntry[];
emails?: string;
links?: string[];
tags?: string[];
sources?: string[];
origin?: string;
owner?: string;
followers?: string[];
postings?: string[];
createdAt?: number;
archived?: ArchievedEntry;
contact?: string[];
}

export default async function runAction(nango: NangoAction, input: LeverCreateNoteInput): Promise<LeverOpportunity> {
if (!input.perform_as) {
throw new nango.ActionError({
message: 'perform_as is the only required field'
});
}

const endpoint = `/v1/opportunities?perform_as=${input.perform_as}`;

try {
const postData = {
name: input.name,
headline: input.headline,
stage: input.stage,
location: input.location,
phones: input.phones,
emails: input.emails,
links: input.links,
tags: input.tags,
sources: input.sources,
origin: input.origin,
owner: input.owner,
followers: input.followers,
postings: input.postings,
createdAt: input.createdAt,
archived: input.archived,
contact: input.contact
};

const queryParams = Object.entries({
...(input.parse ? { parse: input.parse } : {}),
...(input.perform_as_posting_owner ? { note_id: input.perform_as_posting_owner } : {})
})
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');

const params = queryParams ? `&${queryParams}` : '';
const urlWithParams = `${endpoint}${params}`;

const resp = await nango.post({
endpoint: urlWithParams,
data: postData
});

return {
id: resp.data.data.id,
name: resp.data.data.name,
headline: resp.data.data.headline,
contact: resp.data.data.contact,
emails: resp.data.data.emails,
phones: resp.data.data.phones,
confidentiality: resp.data.data.confidentiality,
location: resp.data.data.location,
links: resp.data.data.links,
archived: resp.data.data.archived,
createdAt: resp.data.data.createdAt,
updatedAt: resp.data.data.updatedAt,
lastInteractionAt: resp.data.data.lastInteractionAt,
lastAdvancedAt: resp.data.data.lastAdvancedAt,
snoozedUntil: resp.data.data.snoozedUntil,
archivedAt: resp.data.data.archivedAt,
archiveReason: resp.data.data.archiveReason,
stage: resp.data.data.stage,
stageChanges: resp.data.data.stageChanges,
owner: resp.data.data.owner,
tags: resp.data.data.tags,
sources: resp.data.data.sources,
origin: resp.data.data.origin,
sourcedBy: resp.data.data.sourcedBy,
applications: resp.data.data.applications,
resume: resp.data.data.resume,
followers: resp.data.data.followers,
urls: resp.data.data.urls,
dataProtection: resp.data.data.dataProtection,
isAnonymized: resp.data.data.isAnonymized,
opportunityLocation: resp.data.data.opportunityLocation
};
} catch (error: any) {
throw new Error(`Error in runAction: ${error.message}`);
}
}
82 changes: 82 additions & 0 deletions integration-templates/lever/lever-opportunities-applications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { LeverOpportunityApplication, NangoSync } from './models';

const LIMIT = 100;

export default async function fetchData(nango: NangoSync) {
let totalRecords = 0;

try {
const opportunities: any[] = await getAllOpportunities(nango);

for (const opportunity of opportunities) {
const endpoint = `/v1/opportunities/${opportunity.id}/applications`;

const config = {
paginate: {
type: 'cursor',
cursor_path_in_response: 'next',
cursor_name_in_request: 'offset',
limit_name_in_request: 'limit',
response_path: 'data',
limit: LIMIT
}
};
for await (const application of nango.paginate({ ...config, endpoint })) {
const mappedApplication: LeverOpportunityApplication[] = application.map(mapApplication) || [];
// Save applications
const batchSize: number = mappedApplication.length;
totalRecords += batchSize;
await nango.log(`Saving batch of ${batchSize} application(s) for opportunity ${opportunity.id} (total application(s): ${totalRecords})`);
await nango.batchSave(mappedApplication, 'LeverOpportunityApplication');
}
}
} catch (error: any) {
throw new Error(`Error in fetchData: ${error.message}`);
}
}

async function getAllOpportunities(nango: NangoSync) {
const records: any[] = [];
const config = {
endpoint: '/v1/opportunities',
paginate: {
type: 'cursor',
cursor_path_in_response: 'next',
cursor_name_in_request: 'offset',
limit_name_in_request: 'limit',
response_path: 'data',
limit: LIMIT
}
};

for await (const recordBatch of nango.paginate(config)) {
records.push(...recordBatch);
}

return records;
}

function mapApplication(application: any): LeverOpportunityApplication {
return {
id: application.id,
opportunityId: application.opportunityId,
candidateId: application.candidateId,
createdAt: application.createdAt,
type: application.type,
posting: application.posting,
postingHiringManager: application.postingHiringManager,
postingOwner: application.postingOwner,
user: application.user,
name: application.name,
email: application.email,
phone: application.phone,
requisitionForHire: application.requisitionForHire,
ownerId: application.ownerId,
hiringManager: application.hiringManager,
company: application.company,
links: application.links,
comments: application.comments,
customQuestions: application.customQuestions,
archived: application.archived
};
}
75 changes: 75 additions & 0 deletions integration-templates/lever/lever-opportunities-feedbacks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { LeverOpportunityFeedback, NangoSync } from './models';

const LIMIT = 100;

export default async function fetchData(nango: NangoSync) {
let totalRecords = 0;

try {
const opportunities: any[] = await getAllOpportunities(nango);

for (const opportunity of opportunities) {
const endpoint = `/v1/opportunities/${opportunity.id}/feedback`;

const config = {
paginate: {
type: 'cursor',
cursor_path_in_response: 'next',
cursor_name_in_request: 'offset',
limit_name_in_request: 'limit',
response_path: 'data',
limit: LIMIT
}
};
for await (const feedback of nango.paginate({ ...config, endpoint })) {
const mappedFeedback: LeverOpportunityFeedback[] = feedback.map(mapFeedback) || [];
// Save feedbacks
const batchSize: number = mappedFeedback.length;
totalRecords += batchSize;
await nango.log(`Saving batch of ${batchSize} feedback(s) for opportunity ${opportunity.id} (total feedback(s): ${totalRecords})`);
await nango.batchSave(mappedFeedback, 'LeverOpportunityFeedback');
}
}
} catch (error: any) {
throw new Error(`Error in fetchData: ${error.message}`);
}
}

async function getAllOpportunities(nango: NangoSync) {
const records: any[] = [];
const config = {
endpoint: '/v1/opportunities',
paginate: {
type: 'cursor',
cursor_path_in_response: 'next',
cursor_name_in_request: 'offset',
limit_name_in_request: 'limit',
response_path: 'data',
limit: LIMIT
}
};

for await (const recordBatch of nango.paginate(config)) {
records.push(...recordBatch);
}

return records;
}

function mapFeedback(feedback: any): LeverOpportunityFeedback {
return {
id: feedback.id,
type: feedback.type,
text: feedback.text,
instructions: feedback.instructions,
fields: feedback.fields,
baseTemplateId: feedback.baseTemplateId,
interview: feedback.interview,
panel: feedback.panel,
user: feedback.user,
createdAt: feedback.createdAt,
completedAt: feedback.completedAt,
updatedAt: feedback.updatedAt,
deletedAt: feedback.deletedAt
};
}
Loading

0 comments on commit 2b24195

Please sign in to comment.