3. Build
We welcome all contributions to Panora; from small UI enhancements to brand new integrations. We love seeing community members level up and give people power-ups!
Adding new Integrations ✨
Make sure you are inside packages/api/src
where the server lives !
You want to add a new 3rd Party not yet supported ? 🧑🎤
Ie: Slack, Hubspot, Jira, Shopify …
First choose wisely which category the 3rd party belongs to among these:
crm
ticketing
accounting
ats
filestorage
hris
marketingautomation
You can find all categories inside packages/shared/src/categories.ts
.
For the sake of the guide, now on we’ll consider adding a 3rd party belonging
to the crm
category.
Actually an integration is built in 2 parts :
- the authentication part (oauth, api key, basic etc) which is built by the Panora team
- the service integration where the mapping with our unified model is created which is what you’ll build in the next steps
Step 2: Build your provider service
You want to map a common object to your new 3rd Party ? 👩🎤
Ie: Contact, Ticket, Deal, Company …
For the sake of this guide, let’s map the common object contact
under crm
category to my3rdParty (in reality it would be a real 3rd party name).
An integration is considered valid when all common objects have been mapped.
Then, after the PR is accepted we’ll be able to set active
field to true
inside CONNECTORS_METADATA
Add a new service to map your common object to your 3rd party
Create a new service folder with the name of your 3rd party. Let’s call it my3rdParty.
cd crm/contact/services/my3rdParty
You’ll now create 3 files :
index.ts
where your service is created and direct interaction with your 3rd party API is handledtypes.ts
where the 3rd party specific API types are definedmappers.ts
where the mapping between our unified common model and the 3rd party one is handled
Create the index.ts file
It must implement the IContactService
interface.
export interface IContactService {
addContact(
contactData: DesunifyReturnType,
linkedUserId: string
): Promise<ApiResponse<OriginalContactOutput>>;
syncContacts(
linkedUserId: string
): Promise<ApiResponse<OriginalContactOutput[]>>;
}
@Injectable()
export class My3rdPartyService implements IContactService {
constructor(
private prisma: PrismaService,
private logger: LoggerService,
private cryptoService: EncryptionService,
private registry: ServiceRegistry,
) {
this.logger.setContext(
CrmObject.contact.toUpperCase() + ':' + My3rdPartyService.name,
);
this.registry.registerService('my3rdParty', this);
}
async addContact(
contactData: 3rdPartyContactInput,
linkedUserId: string,
): Promise<ApiResponse<3rdPartyContactOutput>> {}
async syncContacts(
linkedUserId: string,
): Promise<ApiResponse<3rdPartyContactOutput[]>> {}
}
Check other implementations under /crm/contacts/services
to fill the core functions.
Create the types.ts file
The keen readers may have noticed 3rdPartyContactInput
and 3rdPartyContactOutput
.
This is where types.ts
comes in:
Go to the 3rd party API and insert the correct types asked by the API.
export interface 3rdPartyContact {
//INSERT THE CORRECT TYPE HERE
}
export type 3rdPartyContactInput = Partial<3rdPartyContact>;
export type 3rdPartyContactOutput = 3rdPartyContactInput;
Create the mappers.ts file
Last but not least, inside mappers.ts
you have to build the mappings between our unified common object contact
and your third party specific type 3rdPartyContact
.
It must implement IContactMapper
interface.
export interface IContactMapper {
desunify(
source: UnifiedContactInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[]
): DesunifyReturnType;
unify(
source: OriginalContactOutput | OriginalContactOutput[],
customFieldMappings?: {
slug: string;
remote_id: string;
}[]
): Promise<UnifiedContactOutput | UnifiedContactOutput[]>;
}
export class My3rdPartyMapper implements IContactMapper {
desunify(
source: UnifiedContactInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): 3rdPartyContactInput {}
unify(
source: 3rdPartyContactOutput | 3rdPartyContactOutput[],
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<UnifiedContactOutput | UnifiedContactOutput[]> {}
}
Check other implementations under /crm/contacts/services
to fill the core functions.
Enable your new service
After these 3 files are successfully created and filled, you are ready to fix all dependencies/linting issues that you may have.
To make sure the service is enabled, dependencies and imports must be added.
We built a script that does it in seconds. You can execute the given command from the root directory of Panora.
docker build -t validate_connectors -f ./packages/api/Dockerfile.validate-connectors .
docker run -v "$(pwd):/app/" -e VERTICAL=crm -e OBJECT_TYPE=contact validate_connectors
The script will automatically scan the /crm/contact/services
folder and detect any new service folder so all dependencies and imports are updated across the codebase.
Was this page helpful?