Skip to content

Commit

Permalink
feat: associations (#187)
Browse files Browse the repository at this point in the history
* standardize association naming

* create a union type for association

* hubspot association ids

* hubspot contact -> deal association

* hubspot company -> deal association

* hubspot event -> deal association

* hubspot lead -> deal association

* hubspot task -> deal association

* sfdc note -> deal association

* sfdc task -> deal association

* sfdc event -> deal & event -> contact association

* fix import

* fix import

* zoho note -> deal association

* zoho event -> deal association

* zoho task -> deal association

* zoho contact -> deal association

* zoho company -> deal association

* sfdc company -> deal association

* enable sdk deploy to s3

* sfdc contact -> deal association

* zoho contact -> deal association payload fixed

* minor changes

* Address comments

* Handle errors better

* Handle standard errors

* fern types for association
  • Loading branch information
hvinder authored Aug 16, 2023
1 parent 7003ac6 commit 70996fb
Show file tree
Hide file tree
Showing 27 changed files with 550 additions and 71 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/deploy-js-sdk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ on:
push:
branches:
- main
# paths:
# - packages/js/**
paths:
- packages/js/**

jobs:
build-deploy-js-sdk:
Expand Down
62 changes: 62 additions & 0 deletions fern/api/definition/common/associations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
types:
CompanyAssociation:
properties:
dealId:
type: optional<string>
docs: The id of the deal to associate with company
ContactAssociation:
properties:
dealId:
type: optional<string>
docs: The id of the deal to associate with contact
DealAssociation:
properties:
contactId:
type: optional<string>
docs: The id of the contact to associate with deal
companyId:
type: optional<string>
docs: The id of the company to associate with deal
EventAssociation:
properties:
dealId:
type: optional<string>
docs: The id of the deal to associate with event
contactId:
type: optional<string>
docs: The id of the contact to associate with event
LeadAssociation:
properties:
contactId:
type: optional<string>
docs: The id of the contact to associate with lead
companyId:
type: optional<string>
docs: The id of the company to associate with lead
dealId:
type: optional<string>
docs: The id of the deal to associate with lead
NoteAssociation:
properties:
contactId:
type: optional<string>
docs: The id of the contact to associate with note
companyId:
type: optional<string>
docs: The id of the company to associate with note
leadId:
type: optional<string>
docs: The id of the lead to associate with note
dealId:
type: optional<string>
docs: The id of the deal to associate with note
TaskAssociation:
properties:
dealId:
type: optional<string>
docs: The id of the deal to associate with task
UserAssociation:
properties:
dealId:
type: optional<string>
docs: The id of the deal to associate with user
3 changes: 3 additions & 0 deletions fern/api/definition/common/errors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@ errors:
status-code: 500
type: BaseError
NotFoundError:
status-code: 404
type: BaseError
BadRequestError:
status-code: 400
type: BaseError
16 changes: 13 additions & 3 deletions fern/api/definition/common/unified.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
imports:
associations: ./associations.yml

types:
CommonUnifiedFields:
properties:
Expand All @@ -13,7 +16,6 @@ types:
updatedTimestamp:
type: unknown
docs: The last updated timestamp of the object.
associations: unknown
additional:
type: unknown
docs: Any fields that are not unified yet/non-unifiable come inside this `json` object.
Expand All @@ -32,6 +34,7 @@ types:
email:
type: string
docs: The email of the lead in a CRM.
associations: optional<associations.LeadAssociation>
Deal:
extends: CommonUnifiedFields
properties:
Expand All @@ -40,7 +43,7 @@ types:
docs: The deal amount mentioned in the CRM for this deal.
priority:
type: optional<string>
docs: The priority attached to this deal.
docs: The priority attached to this deal. (not supported by pipedrive)
stage:
type: string
docs: Deal stage in the CRM.
Expand All @@ -49,19 +52,21 @@ types:
docs: The name of the deal in a CRM.
expectedCloseDate:
type: unknown
docs: Expected close date for this deal.
docs: Expected close date for this deal. (not supported by pipedrive search api)
isWon:
type: boolean
docs: Is `true` if the deal is closed (won).
probability:
type: integer
docs: Probability of the deal getting closed, a decimal number between 0 to 1 (inclusive).
associations: optional<associations.DealAssociation>
Note:
extends: CommonUnifiedFields
properties:
content:
type: string
docs: The contents of the note in plain text or HTML.
associations: optional<associations.NoteAssociation>
Company:
extends: CommonUnifiedFields
properties:
Expand All @@ -86,6 +91,7 @@ types:
address:
type: CompanyAddress
docs: Company address.
associations: optional<associations.CompanyAssociation>
CompanyAddress:
properties:
street: optional<string>
Expand All @@ -109,6 +115,7 @@ types:
email:
type: string
docs: The email of the contact in a CRM.
associations: optional<associations.ContactAssociation>
Event:
extends: CommonUnifiedFields
properties:
Expand All @@ -133,6 +140,7 @@ types:
location:
type: string
docs: The location of the event/meeting.
associations: optional<associations.EventAssociation>
Task:
extends: CommonUnifiedFields
properties:
Expand All @@ -151,6 +159,7 @@ types:
dueDate:
type: unknown
docs: The date when this task is due.
associations: optional<associations.TaskAssociation>
User:
extends: CommonUnifiedFields
properties:
Expand All @@ -166,3 +175,4 @@ types:
email:
type: string
docs: The email of a user in a CRM.
associations: optional<associations.UserAssociation>
2 changes: 2 additions & 0 deletions fern/api/definition/crm/contact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ service:
- errors.UnAuthorizedError
- errors.InternalServerError
- errors.NotFoundError
- errors.BadRequestError
updateContact:
docs: Update a contact
method: PATCH
Expand All @@ -98,6 +99,7 @@ service:
- errors.UnAuthorizedError
- errors.InternalServerError
- errors.NotFoundError
- errors.BadRequestError
searchContacts:
docs: Search for contacts
method: POST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ export const DEFAULT_SCOPE = {
[TP_ID.pipedrive]: [],
};

export type ValueOf<T> = T[keyof T];
export type AllAssociation = 'contactId' | 'companyId' | 'leadId' | 'dealId' | 'noteId';
3 changes: 3 additions & 0 deletions packages/backend/constants/typeHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type ValueOf<T> = T[keyof T];

export type Subtype<T, U extends T> = U;
15 changes: 15 additions & 0 deletions packages/backend/helpers/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {
BadRequestError,
InternalServerError,
NotFoundError,
UnAuthorizedError,
} from '../generated/typescript/api/resources/common';

export const isStandardError = (error: any) => {
return (
error instanceof NotFoundError ||
error instanceof BadRequestError ||
error instanceof UnAuthorizedError ||
error instanceof InternalServerError
);
};
78 changes: 65 additions & 13 deletions packages/backend/helpers/hubspot.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,68 @@
import { NoteAssociation } from '../models/unified';
import { AllAssociation } from '../constants/common';

export const getHubspotAssociationObj = (key: NoteAssociation) => {
switch (key) {
case 'dealId': {
return {
associationCategory: 'HUBSPOT_DEFINED',
associationTypeId: 214,
};
break;
}
default: {
return {};
}
export const getHubspotAssociationObj = (
key: AllAssociation,
associateObj: 'note' | 'deal' | 'contact' | 'lead' | 'company' | 'event' | 'task'
) => {
const associationTypeMapping: {
[x in typeof associateObj]: { [y in AllAssociation]: number | undefined };
} = {
note: {
dealId: 214,
companyId: 190,
contactId: 202,
leadId: 202,
noteId: undefined,
},
deal: {
contactId: 3,
leadId: 3,
companyId: 341,
noteId: 213,
dealId: undefined,
},
contact: {
companyId: 279,
dealId: 4,
noteId: 201,
leadId: undefined,
contactId: undefined,
},
lead: {
companyId: 279,
dealId: 4,
noteId: 201,
contactId: undefined,
leadId: undefined,
},
company: {
contactId: 280,
leadId: 280,
dealId: 342,
noteId: 189,
companyId: undefined,
},
event: {
contactId: 200,
leadId: 601,
dealId: 212,
noteId: undefined,
companyId: 188,
},
task: {
contactId: 204,
leadId: undefined,
dealId: 216,
noteId: undefined,
companyId: 192,
},
};
const associationTypeId = associationTypeMapping[associateObj][key];
if (associationTypeId) {
return {
associationCategory: 'HUBSPOT_DEFINED',
associationTypeId,
};
}
return null;
};
22 changes: 21 additions & 1 deletion packages/backend/models/unified/company.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { TP_ID } from '@prisma/client';
import { PipedriveCompany } from '../../constants/pipedrive';
import { Subtype } from '../../constants/typeHelpers';
import { AllAssociation } from '../../constants/common';
import { getHubspotAssociationObj } from '../../helpers/hubspot';

export type CompanyAssociation = Subtype<AllAssociation, 'dealId'>;

export interface UnifiedCompany {
name: string;
Expand All @@ -20,7 +25,9 @@ export interface UnifiedCompany {
remoteId: string;
createdTimestamp: Date;
updatedTimestamp: Date;
associations?: any; // TODO: Support associations
associations?: {
[x in CompanyAssociation]?: string;
};
additional: any;
}

Expand Down Expand Up @@ -578,6 +585,19 @@ export function toHubspotCompany(unifiedCompany: UnifiedCompany): Partial<Hubspo
});
}

if (unifiedCompany.associations) {
const associationObj = unifiedCompany.associations;
const associationArr = Object.keys(associationObj).map((key) => {
return {
to: {
id: associationObj[key as CompanyAssociation],
},
types: [getHubspotAssociationObj(key as CompanyAssociation, 'company')],
};
});
hubspotCompany['associations'] = associationArr;
}

return hubspotCompany;
}

Expand Down
25 changes: 23 additions & 2 deletions packages/backend/models/unified/contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { PipedriveContact } from '../../constants/pipedrive';
import { HubspotContact } from '../../constants/hubspot';
import { ZohoContact } from '../../constants/zoho';
import { SalesforceContact } from '../../constants/salesforce';
import { Subtype } from '../../constants/typeHelpers';
import { AllAssociation } from '../../constants/common';
import { getHubspotAssociationObj } from '../../helpers/hubspot';

export type ContactAssociation = Subtype<AllAssociation, 'dealId'>;

export interface UnifiedContact {
firstName: string;
Expand All @@ -13,7 +18,9 @@ export interface UnifiedContact {
remoteId: string; // TODO: Make this unique.
createdTimestamp: Date;
updatedTimestamp: Date;
associations?: any; // TODO: Support associations
associations?: {
[x in ContactAssociation]?: string;
};
additional: any;
}

Expand Down Expand Up @@ -113,7 +120,9 @@ export function toZohoContact(unifiedContact: UnifiedContact): ZohoContact {
// Map custom fields
if (unifiedContact.additional) {
Object.keys(unifiedContact.additional).forEach((key) => {
zohoContact.data[0][key] = unifiedContact.additional?.[key];
if (key !== 'Contact_Role') {
zohoContact.data[0][key] = unifiedContact.additional?.[key];
}
});
}

Expand All @@ -137,6 +146,18 @@ export function toHubspotContact(unifiedContact: UnifiedContact): Partial<Hubspo
hubspotContact['properties'][key] = unifiedContact.additional?.[key];
});
}
if (unifiedContact.associations) {
const associationObj = unifiedContact.associations;
const associationArr = Object.keys(associationObj).map((key) => {
return {
to: {
id: associationObj[key as ContactAssociation],
},
types: [getHubspotAssociationObj(key as ContactAssociation, 'contact')],
};
});
hubspotContact['associations'] = associationArr;
}

return hubspotContact;
}
Expand Down
Loading

1 comment on commit 70996fb

@vercel
Copy link

@vercel vercel bot commented on 70996fb Aug 16, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

revert-client – ./

revert-client-revertdev.vercel.app
app.revert.dev
revert-client-git-main-revertdev.vercel.app

Please sign in to comment.