Skip to content

Commit

Permalink
✅ Testing zendesk realtime webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
naelob committed Jun 12, 2024
1 parent 4330a4d commit f69c793
Show file tree
Hide file tree
Showing 28 changed files with 341 additions and 52 deletions.
32 changes: 16 additions & 16 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,22 +188,22 @@ services:
volumes:
- .:/app

ngrok:
image: ngrok/ngrok:latest
restart: always
command:
- "start"
- "--all"
- "--config"
- "/etc/ngrok.yml"
volumes:
- ./ngrok.yml:/etc/ngrok.yml
ports:
- 4040:4040
depends_on:
api:
condition: service_healthy
network_mode: "host"
#ngrok:
#image: ngrok/ngrok:latest
#restart: always
#command:
# - "start"
# - "--all"
# - "--config"
# - "/etc/ngrok.yml"
# volumes:
# - ./ngrok.yml:/etc/ngrok.yml
# ports:
# - 4040:4040
#depends_on:
# api:
# condition: service_healthy
# network_mode: "host"

docs:
build:
Expand Down
6 changes: 5 additions & 1 deletion packages/api/src/ticketing/@webhook/handler.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import { TicketingWebhookHandlerService } from './handler.service';
import { ZendeskHandlerService } from './zendesk/handler';
import { EnvironmentService } from '@@core/environment/environment.service';
import { EncryptionService } from '@@core/encryption/encryption.service';
import { TicketModule } from '@ticketing/ticket/ticket.module';
import { UserModule } from '@ticketing/user/user.module';
import { AccountModule } from '@ticketing/account/account.module';
import { ContactModule } from '@ticketing/contact/contact.module';

@Module({
imports: [],
imports: [TicketModule, UserModule, AccountModule, ContactModule],
providers: [
PrismaService,
LoggerService,
Expand Down
97 changes: 96 additions & 1 deletion packages/api/src/ticketing/@webhook/zendesk/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import { LoggerService } from '@@core/logger/logger.service';
import { PrismaService } from '@@core/prisma/prisma.service';
import { Injectable } from '@nestjs/common';
import axios from 'axios';
import { Payload } from './types';
import { mapToRemoteEvent } from './utils';
import * as crypto from 'crypto';
import { NonTicketPayload, Payload } from './types';
import { SyncService as TicketSyncService } from '@ticketing/ticket/sync/sync.service';
import { SyncService as UserSyncService } from '@ticketing/user/sync/sync.service';
import { SyncService as ContactSyncService } from '@ticketing/contact/sync/sync.service';
import { SyncService as AccountSyncService } from '@ticketing/account/sync/sync.service';

@Injectable()
export class ZendeskHandlerService {
Expand All @@ -15,6 +19,10 @@ export class ZendeskHandlerService {
private prisma: PrismaService,
private cryptoService: EncryptionService,
private env: EnvironmentService,
private syncTicketsService: TicketSyncService,
private syncUsersService: UserSyncService,
private syncContactsService: ContactSyncService,
private syncAccountsService: AccountSyncService,
) {
this.logger.setContext(ZendeskHandlerService.name);
}
Expand Down Expand Up @@ -244,17 +252,104 @@ export class ZendeskHandlerService {
payload,
id_managed_webhook,
);
const mw = await this.prisma.managed_webhooks.findUnique({
where: {
id_managed_webhook: id_managed_webhook,
},
});
const connection = await this.prisma.connections.findUnique({
where: {
id_connection: mw.id_connection,
},
});
if ('ticketId' in payload) {
// ticket payload
// TODO:update the tickzt inside our db
await this.syncTicketsService.syncTicketsForLinkedUser(
connection.provider_slug.toLowerCase(),
connection.id_linked_user,
connection.id_project,
{
action: 'UPDATE',
data: { remote_id: payload.ticketId as string },
},
);
} else {
//non-ticket payload
const payload_ = payload as NonTicketPayload;
const [event_type, event_action] = this.extractValue(payload_.type);
switch (event_type) {
case 'user':
if (payload_.detail.role) {
if (payload_.detail.role == 'end-user') {
await this.syncContactsService.syncContactsForLinkedUser(
connection.provider_slug.toLowerCase(),
connection.id_linked_user,
connection.id_project,
payload_.detail.id,
{
action:
event_action.toLowerCase() == 'deleted'
? 'DELETE'
: 'UPDATE',
data: { remote_id: payload_.detail.id as string },
},
);
} else if (
payload_.detail.role == 'admin' ||
payload_.detail.role == 'agent'
) {
await this.syncUsersService.syncUsersForLinkedUser(
connection.provider_slug.toLowerCase(),
connection.id_linked_user,
connection.id_project,
{
action:
event_action.toLowerCase() == 'deleted'
? 'DELETE'
: 'UPDATE',
data: { remote_id: payload_.detail.id as string },
},
);
} else {
break;
}
}
break;
case 'organization':
await this.syncAccountsService.syncAccountsForLinkedUser(
connection.provider_slug.toLowerCase(),
connection.id_linked_user,
connection.id_project,
{
action:
event_action.toLowerCase() == 'deleted' ? 'DELETE' : 'UPDATE',
data: { remote_id: payload_.detail.id as string },
},
);
default:
break;
}
}
} catch (error) {
throw new Error(error);
}
}

extractValue(typeString: string): string[] {
const prefix = 'zen:event-type:';
const startIndex = typeString.indexOf(prefix);

if (startIndex === -1) {
throw new Error('Prefix not found in the string.');
}

const afterPrefix = typeString.substring(startIndex + prefix.length);
const values = afterPrefix.split(':');
return [values[0], values[1]];
}


async verifyWebhookAuthenticity(
signature: string,
timestamp: string,
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/ticketing/account/services/front/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class FrontService implements IAccountService {

async syncAccounts(
linkedUserId: string,
remote_account_id?: string,
): Promise<ApiResponse<FrontAccountOutput[]>> {
try {
const connection = await this.prisma.connections.findFirst({
Expand Down
10 changes: 6 additions & 4 deletions packages/api/src/ticketing/account/services/zendesk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class ZendeskService implements IAccountService {

async syncAccounts(
linkedUserId: string,
custom_properties?: string[],
remote_account_id?: string,
): Promise<ApiResponse<ZendeskAccountOutput[]>> {
try {
const connection = await this.prisma.connections.findFirst({
Expand All @@ -38,9 +38,9 @@ export class ZendeskService implements IAccountService {
vertical: 'ticketing',
},
});

const request_url = remote_account_id ? `${connection.account_url}/organizations/${remote_account_id}.json` : `${connection.account_url}/organizations.json`;
const resp = await axios.get(
`${connection.account_url}/organizations.json`,
request_url,
{
headers: {
'Content-Type': 'application/json',
Expand All @@ -52,8 +52,10 @@ export class ZendeskService implements IAccountService {
);
this.logger.log(`Synced zendesk accounts !`);

const result = remote_account_id ? [resp.data.organization] : resp.data.organizations;

return {
data: resp.data.organizations,
data: result,
message: 'Zendesk accounts retrieved',
statusCode: 200,
};
Expand Down
49 changes: 47 additions & 2 deletions packages/api/src/ticketing/account/sync/sync.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ export class SyncService implements OnModuleInit {
integrationId: string,
linkedUserId: string,
id_project: string,
wh_real_time_trigger?: {
action: 'UPDATE' | 'DELETE';
data: {
remote_id: string;
};
},
) {
try {
this.logger.log(
Expand Down Expand Up @@ -152,8 +158,28 @@ export class SyncService implements OnModuleInit {

const service: IAccountService =
this.serviceRegistry.getService(integrationId);
const resp: ApiResponse<OriginalAccountOutput[]> =
await service.syncAccounts(linkedUserId, remoteProperties);

let resp: ApiResponse<OriginalAccountOutput[]>;
if (wh_real_time_trigger && wh_real_time_trigger.data.remote_id) {
//meaning the call has been from a real time webhook that received data from a 3rd party
switch (wh_real_time_trigger.action) {
case 'DELETE':
return await this.removeAccountInDb(
linkedUserId,
integrationId,
wh_real_time_trigger.data.remote_id,
);
default:
resp = await service.syncAccounts(
linkedUserId,
wh_real_time_trigger.data.remote_id,
remoteProperties,
);
break;
}
} else {
resp = await service.syncAccounts(linkedUserId, undefined, remoteProperties);
}

const sourceObject: OriginalAccountOutput[] = resp.data;
// this.logger.log('resp is ' + sourceObject);
Expand Down Expand Up @@ -320,4 +346,23 @@ export class SyncService implements OnModuleInit {
handleServiceError(error, this.logger);
}
}

async removeAccountInDb(
linkedUserId: string,
originSource: string,
remote_id: string,
) {
const existingAccount = await this.prisma.tcg_accounts.findFirst({
where: {
remote_id: remote_id,
remote_platform: originSource,
id_linked_user: linkedUserId,
},
});
await this.prisma.tcg_accounts.delete({
where: {
id_tcg_account: existingAccount.id_tcg_account,
},
});
}
}
1 change: 1 addition & 0 deletions packages/api/src/ticketing/account/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ApiResponse } from '@@core/utils/types';
export interface IAccountService {
syncAccounts(
linkedUserId: string,
remote_account_id?: string,
custom_properties?: string[],
): Promise<ApiResponse<OriginalAccountOutput[]>>;
}
Expand Down
1 change: 0 additions & 1 deletion packages/api/src/ticketing/contact/services/front/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export class FrontService implements IContactService {

async syncContacts(
linkedUserId: string,
unused_,
remote_account_id: string,
): Promise<ApiResponse<FrontContactOutput[]>> {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export class GorgiasService implements IContactService {

async syncContacts(
linkedUserId: string,
unused_,
remote_account_id: string,
): Promise<ApiResponse<GorgiasContactOutput[]>> {
try {
Expand Down
8 changes: 5 additions & 3 deletions packages/api/src/ticketing/contact/services/zendesk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class ZendeskService implements IContactService {

async syncContacts(
linkedUserId: string,
custom_properties?: string[],
remote_account_id?: string,
): Promise<ApiResponse<ZendeskContactOutput[]>> {
try {
const connection = await this.prisma.connections.findFirst({
Expand All @@ -38,16 +38,18 @@ export class ZendeskService implements IContactService {
vertical: 'ticketing',
},
});
const request_url = remote_account_id ? `${connection.account_url}/users/${remote_account_id}.json` : `${connection.account_url}/users.json`;

const resp = await axios.get(`${connection.account_url}/users`, {
const resp = await axios.get(request_url, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
});
const contacts: ZendeskContactOutput[] = resp.data.users;

const contacts: ZendeskContactOutput[] = remote_account_id ? [resp.data.user] : resp.data.users;
const filteredContacts = contacts.filter(
(contact) => contact.role === 'end-user',
);
Expand Down
Loading

0 comments on commit f69c793

Please sign in to comment.