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

Fix Sync Feature #467

Merged
merged 3 commits into from
Jun 4, 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
6 changes: 1 addition & 5 deletions docs/api-reference/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@ Our unified API has predictable resource-oriented URLs, accepts form-encoded req
## Base URL

<Tabs>
<Tab title="US Servers">
<Tab title="Cloud">
```https://api.panora.dev/```
</Tab>

<Tab title="EU Servers">
```https://api-eu.panora.dev/```
</Tab>

<Tab title="Self-Hosted">
```http://localhost:3000/```
</Tab>
Expand Down
Binary file added docs/logo/fav.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/logo/favicon.png
Binary file not shown.
17 changes: 13 additions & 4 deletions docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
"analytics": {
"posthog": {
"apiKey": "phc_WhWJfNPOHAuWVdyTacGxrPa9JW54scnofA9KVEjFcFw"
}
},
"hotjar": {
"hjid": "5154921",
"hjsv": "e2Bs3cIPcF08ngtrjwX+JBB8"
}
},
"api": {
"baseUrl": "https://api.panora.dev",
Expand All @@ -20,11 +24,16 @@
"default": "light",
"isHidden": false
},
"feedback": {
"thumbsRating": true,
"suggestEdit": true,
"raiseIssue": true
},
"logo": {
"dark": "/logo/logo-panora-white-hq.png",
"light": "/logo/logo-panora-white.png"
},
"favicon": "logo/favicon.png",
"favicon": "logo/fav.png",
"colors": {
"primary": "#085faf",
"light": "#085faf",
Expand All @@ -50,7 +59,7 @@
},
"tabs": [
{
"name": "Crm",
"name": "CRM",
"url": "crm"
},
{
Expand Down Expand Up @@ -162,7 +171,7 @@
"group": "Unified Objects",
"pages": [
{
"group": "Crm",
"group": "CRM",
"pages": [
{
"group": "Contact",
Expand Down
8 changes: 4 additions & 4 deletions packages/api/scripts/connectorUpdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ function updateEnumFile(enumFilePath, newServiceDirs, vertical) {
const base = vertical.substring(0, 1).toUpperCase() + vertical.substring(1);

// Define the enum name to be updated based on the vertical
let enumName = `${base}Providers`;
let enumName = `${base}Connectors`;
// Extract the enum content
const enumRegex = new RegExp(`export enum ${enumName} {([\\s\\S]*?)}\n`, 'm');
const match = fileContent.match(enumRegex);
Expand Down Expand Up @@ -328,9 +328,9 @@ function updateObjectTypes(baseDir, objectType, vertical) {
// Extract the current provider arrays from providers.ts and enum.ts
const providersFilePath = path.join(
__dirname,
'../../shared/src/providers.ts',
'../../shared/src/connectors/index.ts',
);
const enumFilePath = path.join(__dirname, '../../shared/src/enum.ts');
const enumFilePath = path.join(__dirname, '../../shared/src/connectors/enum.ts');
const currentProviders = extractArrayFromFile(
providersFilePath,
`${vertical.toUpperCase()}_PROVIDERS`,
Expand Down Expand Up @@ -377,7 +377,7 @@ function updateObjectTypes(baseDir, objectType, vertical) {
baseDir,
objectType,
);
console.log(importStatements)
// console.log(importStatements)
updateTargetFile(targetFile, importStatements, possibleProviderForImportStatements, objectType);
}

Expand Down
14 changes: 9 additions & 5 deletions packages/api/src/@core/connections/connections.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ConnectorCategory } from '@panora/shared';
import { AccountingConnectionsService } from './accounting/services/accounting.connection.service';
import { MarketingAutomationConnectionsService } from './marketingautomation/services/marketingautomation.connection.service';
import { JwtAuthGuard } from '@@core/auth/guards/jwt-auth.guard';
import { CoreSyncService } from '@@core/sync/sync.service';

export type StateDataType = {
projectId: string;
Expand All @@ -34,6 +35,7 @@ export class ConnectionsController {
private readonly ticketingConnectionsService: TicketingConnectionsService,
private readonly accountingConnectionsService: AccountingConnectionsService,
private readonly marketingAutomationConnectionsService: MarketingAutomationConnectionsService,
private readonly coreSyncService: CoreSyncService,
private logger: LoggerService,
private prisma: PrismaService,
) {
Expand All @@ -49,7 +51,7 @@ export class ConnectionsController {
@ApiQuery({ name: 'location', required: true, type: String })
@ApiResponse({ status: 200 })
@Get('oauth/callback')
handleCallback(
async handleCallback(
@Res() res: Response,
@Query('state') state: string,
@Query('code') code: string,
Expand All @@ -71,7 +73,7 @@ export class ConnectionsController {
switch (vertical.toLowerCase()) {
case ConnectorCategory.Crm:
const zohoLocation_ = zohoLocation ? zohoLocation : '';
this.crmConnectionsService.handleCRMCallBack(
await this.crmConnectionsService.handleCRMCallBack(
projectId,
linkedUserId,
providerName,
Expand All @@ -82,7 +84,7 @@ export class ConnectionsController {
case ConnectorCategory.Ats:
break;
case ConnectorCategory.Accounting:
this.accountingConnectionsService.handleAccountingCallBack(
await this.accountingConnectionsService.handleAccountingCallBack(
projectId,
linkedUserId,
providerName,
Expand All @@ -94,22 +96,24 @@ export class ConnectionsController {
case ConnectorCategory.Hris:
break;
case ConnectorCategory.MarketingAutomation:
this.marketingAutomationConnectionsService.handleMarketingAutomationCallBack(
await this.marketingAutomationConnectionsService.handleMarketingAutomationCallBack(
projectId,
linkedUserId,
providerName,
code,
);
break;
case ConnectorCategory.Ticketing:
this.ticketingConnectionsService.handleTicketingCallBack(
await this.ticketingConnectionsService.handleTicketingCallBack(
projectId,
linkedUserId,
providerName,
code,
);
break;
}
// Performing Core Sync Service
this.coreSyncService.initialSync(vertical.toLowerCase(), providerName, linkedUserId, projectId);
res.redirect(returnUrl);
} catch (error) {
handleServiceError(error, this.logger);
Expand Down
37 changes: 35 additions & 2 deletions packages/api/src/@core/connections/connections.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ import { TicketingConnectionModule } from './ticketing/ticketing.connection.modu
import { AccountingConnectionModule } from './accounting/accounting.connection.module';
import { MarketingAutomationConnectionsModule } from './marketingautomation/marketingautomation.connection.module';
import { ValidateUserService } from '@@core/utils/services/validateUser.service';
import { CoreSyncService } from '@@core/sync/sync.service';
import { CompanyModule } from '@crm/company/company.module';
import { ContactModule } from '@crm/contact/contact.module';
import { DealModule } from '@crm/deal/deal.module';
import { EngagementModule } from '@crm/engagement/engagement.module';
import { NoteModule } from '@crm/note/note.module';
import { StageModule } from '@crm/stage/stage.module';
import { TaskModule } from '@crm/task/task.module';
import { UserModule } from '@crm/user/user.module';
import { AccountModule } from '@ticketing/account/account.module';
import { CollectionModule } from '@ticketing/collection/collection.module';
import { CommentModule } from '@ticketing/comment/comment.module';
import { ContactModule as TContactModule } from '@ticketing/contact/contact.module';
import { TagModule } from '@ticketing/tag/tag.module';
import { TeamModule } from '@ticketing/team/team.module';
import { TicketModule } from '@ticketing/ticket/ticket.module';
import { UserModule as TUserModule } from '@ticketing/user/user.module';

@Module({
controllers: [ConnectionsController],
Expand All @@ -15,13 +32,29 @@ import { ValidateUserService } from '@@core/utils/services/validateUser.service'
TicketingConnectionModule,
AccountingConnectionModule,
MarketingAutomationConnectionsModule,
CompanyModule,
ContactModule,
DealModule,
EngagementModule,
NoteModule,
StageModule,
TaskModule,
UserModule,
AccountModule,
CollectionModule,
CommentModule,
TContactModule,
TagModule,
TeamModule,
TicketModule,
TUserModule
],
providers: [LoggerService, PrismaService, ValidateUserService],
providers: [LoggerService, PrismaService, ValidateUserService, CoreSyncService],
exports: [
CrmConnectionModule,
TicketingConnectionModule,
AccountingConnectionModule,
MarketingAutomationConnectionsModule,
],
})
export class ConnectionsModule {}
export class ConnectionsModule { }
96 changes: 96 additions & 0 deletions packages/api/src/@core/sync/sync.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Injectable } from '@nestjs/common';
import { LoggerService } from '../logger/logger.service';
import { ConnectorCategory } from '@panora/shared';
import { CrmObject, ENGAGEMENTS_TYPE } from '@crm/@lib/@types';
import { PrismaService } from '@@core/prisma/prisma.service';
import { handleServiceError } from '@@core/utils/errors';
import { SyncService as CrmCompanySyncService } from '@crm/company/sync/sync.service';
import { SyncService as CrmContactSyncService } from '@crm/contact/sync/sync.service';
Expand All @@ -22,6 +25,7 @@ import { SyncService as TicketingUserSyncService } from '@ticketing/user/sync/sy
export class CoreSyncService {
constructor(
private logger: LoggerService,
private prisma: PrismaService,
private CrmCompanySyncService: CrmCompanySyncService,
private CrmContactSyncService: CrmContactSyncService,
private CrmDealSyncService: CrmDealSyncService,
Expand All @@ -42,9 +46,101 @@ export class CoreSyncService {
this.logger.setContext(CoreSyncService.name);
}

Copy link

Choose a reason for hiding this comment

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

Add a comment explaining the purpose of the initialSync method.

//Initial sync which will execute when connection is successfully established
async initialSync(
vertical: string,
provider: string,
linkedUserId: string,
id_project: string
) {
try {

const tasks = [];

switch (vertical) {
case ConnectorCategory.Crm:
// logic
tasks.push(() => this.CrmUserSyncService.syncUsersForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.CrmCompanySyncService.syncCompaniesForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.CrmContactSyncService.syncContactsForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.CrmDealSyncService.syncDealsForLinkedUser(provider, linkedUserId, id_project));

for (const type of ENGAGEMENTS_TYPE) {
tasks.push(() => this.CrmEngagementSyncService.syncEngagementsForLinkedUser(provider, linkedUserId, id_project, type));
}

tasks.push(() => this.CrmNoteSyncService.syncNotesForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.CrmTaskSyncService.syncTasksForLinkedUser(provider, linkedUserId, id_project));

for (const task of tasks) {
try {
await task();
} catch (error) {
handleServiceError(error, this.logger);
}
}

const deals = await this.prisma.crm_deals.findMany({
where: {
remote_platform: provider,
id_linked_user: linkedUserId,
},
});
for (const deal of deals) {
await this.CrmStageSyncService.syncStagesForLinkedUser(
provider,
linkedUserId,
id_project,
deal.id_crm_deal,
);
}
break;

case ConnectorCategory.Ticketing:
// logic
tasks.push(() => this.TicketingUserSyncService.syncUsersForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.TicketingAccountSyncService.syncAccountsForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.TicketingCollectionSyncService.syncCollectionsForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.TicketingTicketSyncService.syncTicketsForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.TicketingTeamSyncService.syncTeamsForLinkedUser(provider, linkedUserId, id_project));
tasks.push(() => this.TicketingContactSyncService.syncContactsForLinkedUser(provider, linkedUserId, id_project));

for (const task of tasks) {
try {
await task();
} catch (error) {
handleServiceError(error, this.logger);
}
}

const tickets = await this.prisma.tcg_tickets.findMany({
where: {
remote_platform: provider,
id_linked_user: linkedUserId,
},
});

for (const ticket of tickets) {
try {
await this.TicketingCommentSyncService.syncCommentsForLinkedUser(provider, linkedUserId, id_project, ticket.id_tcg_ticket);
await this.TicketingTagSyncService.syncTagsForLinkedUser(provider, linkedUserId, id_project, ticket.id_tcg_ticket);
} catch (error) {
handleServiceError(error, this.logger);
}
}

break;
}

} catch (error) {
handleServiceError(error, this.logger);
}
}

// we must have a sync_jobs table with 7 (verticals) rows, one of each is syncing details
async getSyncStatus(vertical: string) {
try {

} catch (error) {
handleServiceError(error, this.logger);
}
Expand Down
16 changes: 12 additions & 4 deletions packages/api/src/@core/utils/types/original/original.ticketing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ export type OriginalUserInput =
| JiraUserInput;
//| JiraServiceMgmtUserInput;
/* account */
export type OriginalAccountInput = ZendeskAccountInput | FrontAccountInput;
export type OriginalAccountInput =
| ZendeskAccountInput
| FrontAccountInput;
/* contact */
export type OriginalContactInput =
| ZendeskContactInput
Expand All @@ -178,7 +180,9 @@ export type OriginalTeamInput =

/* attachment */
export type OriginalAttachmentInput = null;
export type OriginalCollectionInput = JiraCollectionInput | GitlabCollectionInput;
export type OriginalCollectionInput =
| JiraCollectionInput
| GitlabCollectionInput;

export type TicketingObjectInput =
| OriginalTicketInput
Expand Down Expand Up @@ -217,7 +221,9 @@ export type OriginalUserOutput =
| GorgiasUserOutput
| JiraUserOutput;
/* account */
export type OriginalAccountOutput = ZendeskAccountOutput | FrontAccountOutput;
export type OriginalAccountOutput =
| ZendeskAccountOutput
| FrontAccountOutput;
/* contact */
export type OriginalContactOutput =
| ZendeskContactOutput
Expand Down Expand Up @@ -247,7 +253,9 @@ export type OriginalAttachmentOutput =

/* collection */

export type OriginalCollectionOutput = JiraCollectionOutput | GitlabCollectionOutput;
export type OriginalCollectionOutput =
| JiraCollectionOutput
| GitlabCollectionOutput;


export type TicketingObjectOutput =
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/ticketing/@lib/@utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export class Utils {
id_tcg_collection: uuid,
Copy link

Choose a reason for hiding this comment

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

Returning undefined instead of throwing an error changes the method's behavior. Ensure that all calling code handles undefined appropriately.

},
});
if (!res) throw new Error(`tcg_contact not found for uuid ${uuid}`);
if (!res) return;
return res.remote_id;
} catch (error) {
throw new Error(error);
Expand Down
Loading
Loading