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(aircall): add user operations #77

Merged
merged 4 commits into from
Oct 28, 2024
Merged
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
37 changes: 36 additions & 1 deletion flows.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,39 @@
integrations:
aircall-basic:
actions:
create-user:
description: Creates a user in Aircall.
output: User
endpoint: POST /users
input: CreateUser
delete-user:
description: Deletes a user in Aircall
endpoint: DELETE /users
output: SuccessResponse
input: IdEntity
syncs:
users:
runs: every day
description: |
Fetches a list of users from Aircall.
output: User
track_deletes: true
sync_type: full
endpoint: GET /users
models:
SuccessResponse:
success: boolean
IdEntity:
id: string
User:
id: string
email: string
firstName: string
lastName: string
CreateUser:
firstName: string
lastName: string
email: string
airtable:
syncs:
tables:
Expand Down Expand Up @@ -1996,7 +2031,7 @@ integrations:
description?: string
ExactInvoiceCreateInput:
customerId: string
journal: number
journal?: number
currency?: EUR
description?: string
createdAt?: date
Expand Down
43 changes: 43 additions & 0 deletions integrations/aircall-basic/actions/create-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { NangoAction, ProxyConfiguration, CreateUser, User } from '../../models';
import { createUserSchema } from '../schema.zod.js';
import type { AircallUser } from '../types';

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 aInput = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aInput? 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, a short for Aircall

email: input.email,
first_name: input.firstName,
last_name: input.lastName
};

const config: ProxyConfiguration = {
// https://developer.aircall.io/api-references/#create-a-user
endpoint: '/v1/users',
data: aInput,
retries: 10
};

const response = await nango.post<{ user: AircallUser }>(config);

const { data } = response;

const [firstName, lastName] = data.user.name.split(' ');
const user: User = {
id: data.user.id.toString(),
email: data.user.email,
firstName: firstName || '',
lastName: lastName || ''
};

return user;
}
21 changes: 21 additions & 0 deletions integrations/aircall-basic/actions/delete-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { NangoAction, ProxyConfiguration, SuccessResponse, IdEntity } from '../../models';

export default async function runAction(nango: NangoAction, input: IdEntity): Promise<SuccessResponse> {
if (!input || !input.id) {
throw new nango.ActionError({
message: 'Id is required'
});
}

const config: ProxyConfiguration = {
// https://developer.aircall.io/api-references/#delete-a-user
endpoint: `/v1/users/${input.id}`,
retries: 10
};

await nango.delete(config);

return {
success: true
};
}
5 changes: 5 additions & 0 deletions integrations/aircall-basic/fixtures/create-user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"firstName": "John",
"lastName": "Doe",
"email": "johndoe@test.dev"
}
5 changes: 5 additions & 0 deletions integrations/aircall-basic/mocks/create-user/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"firstName": "John",
"lastName": "Doe",
"email": "johndoe@test.dev"
}
6 changes: 6 additions & 0 deletions integrations/aircall-basic/mocks/create-user/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"id": "1435616",
"email": "johndoe@test.dev",
"firstName": "John",
"lastName": "Doe"
}
3 changes: 3 additions & 0 deletions integrations/aircall-basic/mocks/delete-user/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"id": "1435616"
}
3 changes: 3 additions & 0 deletions integrations/aircall-basic/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 @@
""
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"users": [
{
"id": 1434499,
"direct_link": "https://api.aircall.io/v1/users/1434499",
"name": "Johnny Doeseph",
"email": "johnny@some-company.com",
"available": false,
"availability_status": "available",
"created_at": "2024-10-25T12:59:36.000Z",
"time_zone": "Etc/UTC",
"language": "en-US",
"state": "always_opened",
"wrap_up_time": 0
},
{
"id": 1435616,
"direct_link": "https://api.aircall.io/v1/users/1435616",
"name": "John Doe",
"email": "johndoe@test.dev",
"available": false,
"availability_status": "available",
"created_at": "2024-10-28T09:24:52.000Z",
"time_zone": "Etc/UTC",
"language": "en-US",
"state": "always_opened",
"wrap_up_time": 0
}
],
"meta": {
"count": 2,
"total": 2,
"current_page": 1,
"per_page": 20,
"next_page_link": null,
"previous_page_link": null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"user": {
"id": 1435616,
"direct_link": "https://api.aircall.io/v1/users/1435616",
"name": "John Doe",
"email": "johndoe@test.dev",
"available": false,
"availability_status": "available",
"created_at": "2024-10-28T09:24:52.000Z",
"time_zone": "Etc/UTC",
"language": "en-US",
"state": "always_opened",
"wrap_up_time": 0,
"default_number_id": null,
"numbers": [],
"substatus": "always_opened"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
14 changes: 14 additions & 0 deletions integrations/aircall-basic/mocks/users/User/batchSave.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"id": "1434499",
"firstName": "Johnny",
"lastName": "Doeseph",
"email": "johnny@some-company.com"
},
{
"id": "1435616",
"firstName": "John",
"lastName": "Doe",
"email": "johndoe@test.dev"
}
]
40 changes: 40 additions & 0 deletions integrations/aircall-basic/nango.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
integrations:
aircall-basic:
actions:
create-user:
description: Creates a user in Aircall.
output: User
endpoint: POST /users
input: CreateUser
delete-user:
description: Deletes a user in Aircall
endpoint: DELETE /users
output: SuccessResponse
input: IdEntity
syncs:
users:
runs: every day
description: |
Fetches a list of users from Aircall.
output: User
track_deletes: true
sync_type: full
endpoint: GET /users
models:
# Generic
SuccessResponse:
success: boolean
IdEntity:
id: string

# User
User:
id: string
email: string
firstName: string
lastName: string

CreateUser:
firstName: string
lastName: string
email: string
23 changes: 23 additions & 0 deletions integrations/aircall-basic/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 successResponseSchema = z.object({
success: z.boolean()
});

export const idEntitySchema = z.object({
id: z.string()
});

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()
});
29 changes: 29 additions & 0 deletions integrations/aircall-basic/syncs/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { NangoSync, ProxyConfiguration, User } from '../../models';
import type { AircallUser } from '../types';

export default async function fetchData(nango: NangoSync) {
const config: ProxyConfiguration = {
// https://developer.aircall.io/api-references/#list-all-users
endpoint: '/v1/users',
retries: 10,
paginate: {
response_path: 'users'
}
};

for await (const aUsers of nango.paginate<AircallUser>(config)) {
const users: User[] = aUsers.map((aUser: AircallUser) => {
const [firstName, lastName] = aUser.name.split(' ');
const user: User = {
id: aUser.id.toString(),
firstName: firstName || '',
lastName: lastName || '',
email: aUser.email
};

return user;
});

await nango.batchSave(users, 'User');
}
}
19 changes: 19 additions & 0 deletions integrations/aircall-basic/tests/aircall-basic-create-user.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { vi, expect, it, describe } from "vitest";

import runAction from "../actions/create-user.js";

describe("aircall-basic create-user tests", () => {
const nangoMock = new global.vitest.NangoActionMock({
dirname: __dirname,
name: "create-user",
Model: "User"
});

it('should output the action output that is expected', async () => {
const input = await nangoMock.getInput();
const response = await runAction(nangoMock, input);
const output = await nangoMock.getOutput();

expect(response).toEqual(output);
});
});
19 changes: 19 additions & 0 deletions integrations/aircall-basic/tests/aircall-basic-delete-user.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { vi, expect, it, describe } from "vitest";

import runAction from "../actions/delete-user.js";

describe("aircall-basic delete-user tests", () => {
const nangoMock = new global.vitest.NangoActionMock({
dirname: __dirname,
name: "delete-user",
Model: "SuccessResponse"
});

it('should output the action output that is expected', async () => {
const input = await nangoMock.getInput();
const response = await runAction(nangoMock, input);
const output = await nangoMock.getOutput();

expect(response).toEqual(output);
});
});
Loading