keap-client
Version:
A client to work with Infusionsoft/Keap API.
563 lines (527 loc) • 17.2 kB
text/typescript
import { Api } from "../utils/api";
import { Paginator } from "../utils/paginator";
import { createParams } from "../utils/queryParams";
export class Contacts {
private api: Api;
/**
* Creates a new Contact model.
* @param api - The API client to use for making requests.
*/
constructor(api: Api) {
this.api = api;
}
/**
* Fetches a list of contacts with optional filtering.
* @param parameters - Options for fetching contacts.
* @param parameters.email - Optional email address to query on.
* @param parameters.family_name - Optional last name or surname to query on.
* @param parameters.given_name - Optional first name or forename to query on.
* @param parameters.limit - Sets a total of items to return.
* @param parameters.offset - Sets a beginning range of items to return.
* @param parameters.optional_properties - List of Contact properties to include in the response.
* @param parameters.order - Attribute to order items by.
* @param parameters.order_direction - How to order the data (ascending or descending).
* @param parameters.since - Date to start searching from on LastUpdated (ex. 2017-01-01T22:17:59.039Z).
* @param parameters.until - Date to search to on LastUpdated (ex. 2017-01-01T22:17:59.039Z).
* @example
* const contacts = await contactsInstance.getContacts({
* limit: 5,
* offset: 10,
* order: "date_created",
* order_direction: "DESCENDING"
* });
* @returns A paginator containing the list of contacts.
*/
async getContacts(parameters?: {
email?: string;
family_name?: string;
given_name?: string;
limit?: number;
offset?: number;
optional_properties?: string[];
order?:
| "id"
| "date_created"
| "last_updated"
| "name"
| "firstName"
| "email";
order_direction?: "ASCENDING" | "DESCENDING";
since?: string;
until?: string;
}): Promise<Paginator<IContact>> {
const params = parameters
? createParams(parameters, [
"email",
"limit",
"offset",
"family_name",
"given_name",
"optional_properties",
"order",
"order_direction",
"since",
"until",
]).toString()
: "";
const response = await this.api.get(`v1/contacts?${params}`);
const responseObj = response as { contacts: IContact[] | null };
if (
!responseObj ||
!Object.prototype.hasOwnProperty.call(responseObj, "contacts")
) {
throw new Error("Invalid response format: missing contacts property");
}
if (responseObj.contacts === null) {
throw new Error("No contacts found in response");
}
const contacts = responseObj.contacts.map((contact: IContact) => {
if (contact === null) {
throw new Error("Contact data is null");
}
return new Contact(this, contact);
});
return Paginator.wrap(this.api, { ...response, contacts }, "contacts");
}
/**
* Creates a new contact in Keap.
* Note: Contact must contain at least one item in email_addresses or phone_numbers,
* and country_code is required if region is specified on the addresses.
* @param contactData - The data to create a contact with.
* @returns The newly created contact.
* @example
* const contact = await contactsInstance.createContact({
* given_name: 'John',
* family_name: 'Doe',
* email_addresses: [
* {
* email: "john.doe@example.com",
* field: "EMAIL1"
* }
* ]
* });
*/
async createContact(contactData: IContact): Promise<Contact> {
if (!contactData.email_addresses && !contactData.phone_numbers) {
throw new Error(
"Contact must contain at least one item in email_addresses or phone_numbers"
);
}
const addressesWithRegionAndNoCountryCode = contactData.addresses?.filter(
(address) => address.region && !address.country_code
);
if (
addressesWithRegionAndNoCountryCode &&
addressesWithRegionAndNoCountryCode.length > 0
) {
throw new Error(
"Contact must contain a country code if a region is provided"
);
}
const response = await this.api.post("v1/contacts", contactData);
return new Contact(this, response as IContact);
}
/**
* Updates an existing contact.
* @param contactId - The ID of the contact to update.
* @param contactData {IContact} - The data to update the contact with.
* @returns The updated contact if the API call was successful.
* @throws Will throw an error if the API call fails.
* @example
* const contact = await contacts
* .updateContact(123, {
* given_name: "Jane",
* family_name: "Doe"
* });
*/
async updateContact(
contactId: number,
contactData: IContact
): Promise<Contact> {
const r = await this.api.patch(`v1/contacts/${contactId}`, contactData);
return new Contact(this, r as IContact);
}
/**
* Deletes a contact by ID.
* @param contactId - The ID of the contact to delete.
* @returns True if the contact was successfully deleted.
* @example
* const result = await contactsInstance.deleteContact(123);
* if (result) {
* console.log("Contact deleted successfully");
* }
*/
async deleteContact(contactId: number): Promise<boolean> {
await this.api.delete(`v1/contacts/${contactId}`);
return true;
}
/**
* Fetches a single contact by ID.
* @param contactId - The ID of the contact to fetch.
* @returns The contact.
* @example
* const contact = await contactsInstance.getContact(123);
* console.log(contact.given_name); // "Jane"
* console.log(contact.family_name); // "Doe"
*/
async getContact(contactId: number): Promise<Contact> {
const response = await this.api.get(`v1/contacts/${contactId}`);
return new Contact(this, response as IContact);
}
/**
* Fetches the contact model schema.
* @returns The contact model.
* @example
* const contactModel = await contactsInstance.getContactModel();
* console.log(contactModel);
*/
async getContactModel(): Promise<ContactModel> {
const response = await this.api.get("v1/contacts/model");
return response as ContactModel;
}
/**
* Creates a new custom contact field.
* @param customFieldData - The data to create a custom field with.
* @returns The newly created custom field.
* @example
* const customField = await contactsInstance.createCustomField({
* field_type: "Text",
* label: "Custom Field Name"
* });
*/
async createCustomField(customFieldData: CustomField): Promise<CustomField> {
const response = await this.api.post(
"v1/contacts/customFields",
customFieldData
);
return response as CustomField;
}
/**
* Creates a new contact if the contact does not exist, or updates the contact if it does.
* @param contactData - The data to create or update a contact with.
* @returns The newly created or updated contact.
* @example
* const contact = await contactsInstance.createOrUpdate({
* given_name: "Jane",
* family_name: "Doe"
* });
* console.log(contact); // {given_name: "Jane", family_name: "Doe", id: 123}
*/
async createOrUpdate(contactData: IContact): Promise<Contact> {
const response = await this.api.put("v1/contacts", contactData);
return new Contact(this, response as IContact);
}
/**
* Fetches the credit cards associated with a contact.
* @param contactId - The ID of the contact to fetch credit cards for.
* @returns The credit cards associated with the contact if the API call was successful.
*/
async getCreditCards(contactId: number): Promise<object> {
const response = await this.api.get(`v1/contacts/${contactId}/creditCards`);
return response as object;
}
/**
* Creates a new credit card for a contact.
* @param contactId - The ID of the contact to add the credit card to.
* @param creditCardData - The data to create the credit card with.
* @returns The newly created credit card.
*/
async createCreditCard(
contactId: number,
creditCardData: object
): Promise<CreditCard> {
const response = await this.api.post(
`v1/contacts/${contactId}/creditCards`,
creditCardData
);
return response as CreditCard;
}
/**
* Fetches the email addresses associated with a contact.
* @param contactId - The ID of the contact to fetch email addresses for.
* @returns The email addresses associated with the contact if the API call was successful.
*/
async listEmails(
contactId: number,
options?: { email?: string; limit?: number; offset?: number }
): Promise<Paginator<EmailRecord>> {
const params = options
? createParams(options, ["email", "limit", "offset"])
: "";
const r = await this.api.get(
`v1/contacts/${contactId}/emails?${params?.toString()}`
);
return Paginator.wrap(this.api, r, "emails");
}
/**
* Creates a new email address for a contact.
* @param contactId - The ID of the contact to add the email address to.
* @param emailData {EmailRecord} - The data to create the email address with.
* @returns The newly created email address if the API call was successful.
*/
createEmail(contactId: number, emailData: EmailRecord): Promise<EmailRecord> {
return this.api.post(
`v1/contacts/${contactId}/emails`,
emailData
) as Promise<EmailRecord>;
}
/**
* Fetches the tags applied to a contact.
* @param contactId - The ID of the contact to fetch tags for.
* @param options - The options to use when fetching tags.
* @returns The tags applied to the contact if the API call was successful.
*/
async listAppliedTags(
contactId: number,
options?: { limit?: number; offset?: number }
): Promise<Paginator<Tag>> {
const params = options ? createParams(options, ["limit", "offset"]) : "";
const r = await this.api.get(
`v1/contacts/${contactId}/tags?${params?.toString()}`
);
return Paginator.wrap(this.api, r, "tags");
}
/**
* Applies a tag to a contact.
* @param contactId {number} - The ID of the contact to apply the tag to.
* @param tagIds {number[]} - The ID of the tag to apply.
* @returns The result of the API call if it was successful.
*/
applyTags(contactId: number, tagIds: number[]): Promise<object> {
return this.api.post(`v1/contacts/${contactId}/tags`, {
tagIds,
}) as Promise<object>;
}
/**
* Removes a tag from a contact.
* @param contactId - The ID of the contact to remove the tag from.
* @param tagId - The ID of the tag to remove.
* @returns The result of the API call if it was successful.
*/
removeTag(contactId: number, tagId: number): Promise<object> {
return this.api.delete(
`v1/contacts/${contactId}/tags/${tagId}`
) as Promise<object>;
}
/**
* Removes multiple tags from a contact.
* @param contactId - The ID of the contact to remove the tags from.
* @param tagIds - The IDs of the tags to remove.
* @param data - Data to be sent with the request.
* @returns The result of the API call if it was successful.
*/
removeTags(
contactId: number,
tagIds: number[],
data: object
): Promise<object> {
return this.api.delete(
`v1/contacts/${contactId}/tags?tagIds=${tagIds.join(",")}`,
data
) as Promise<object>;
}
/**
* Adds UTM tracking data to a contact.
* @param contactId - The ID of the contact to add the UTM data to.
* @param utmData - The UTM data to add.
* @returns The result of the API call if it was successful.
*/
addUTM(contactId: number, utmData: UTM): Promise<UTM> {
return this.api.post(
`v1/contacts/${contactId}/utms`,
utmData
) as Promise<UTM>;
}
}
class Contact {
ScoreValue: string | null = null;
addresses: Array<{
country_code: string;
field: string;
line1: string;
line2: string;
locality: string;
postal_code: string;
region: string;
zip_code: string;
zip_four: string;
}> | null = null;
anniversary: Date | null = null;
birthday: Date | null = null;
company: {
company_name: string;
id: number;
} | null = null;
company_name: string | null = null;
contact_type: string | null = null;
custom_fields: Array<{
content: object;
id: number;
}> | null = null;
date_created: Date | null = null;
email_addresses: Array<{
email: string;
field: string;
}> | null = null;
email_opted_in: boolean | null = null;
email_status: string | null = null;
family_name: string | null = null;
fax_numbers: Array<{
field: string;
number: string;
type: string;
}> | null = null;
given_name: string | null = null;
id: number;
job_title: string | null = null;
last_updated: Date | null = null;
lead_source_id: number | null = null;
middle_name: string | null = null;
opt_in_reason: string | null = null;
origin: {
date: Date;
ip_address: string;
} | null = null;
owner_id: number | null = null;
phone_numbers: Array<{
extension: string;
field: string;
number: string;
type: string;
}> | null = null;
preferred_locale: string | null = null;
preferred_name: string | null = null;
prefix: string | null = null;
relationships: Array<{
id: number;
linked_contact_id: number;
relationship_type_id: number;
}> | null = null;
social_accounts: Array<{
name: string;
type: string;
}> | null = null;
source_type: string | null = null;
spouse_name: string | null = null;
suffix: string | null = null;
tag_ids: Array<number> | null = null;
time_zone: string | null = null;
website: string | null = null;
private contacts: Contacts;
/**
* Creates a new Contact instance from the given data and API client.
* @param api - The API client to use for making requests.
* @param data - The data to use to populate the Contact instance.
*/
constructor(contacts: Contacts, data: IContact) {
this.contacts = contacts;
Object.assign(this, data);
if (!data.id) throw new Error("Contact ID is required");
this.id = data.id;
}
/**
* Updates the contact with the given data.
* @param data - The data to update the contact with.
* @returns The updated contact if the API call was successful.
*/
async update(data: IContact): Promise<Contact> {
const r = await this.contacts.updateContact(this.id, data);
return new Contact(this.contacts, r);
}
/**
* Deletes the contact.
* @returns The result of the API call if it was successful.
* @throws Will throw an error if the API call fails.
*/
async delete(): Promise<boolean> {
return this.contacts.deleteContact(this.id);
}
/**
* Refreshes the contact data from the API.
* @returns The updated contact if the API call was successful.
*/
async refresh(): Promise<Contact> {
const contact = await this.contacts.getContact(this.id);
Object.assign(this, contact);
return contact;
}
/**
* Fetches the credit cards associated with this contact.
* @returns The credit cards associated with this contact if the API call was successful.
*/
getCreditCards(): Promise<object> {
return this.contacts.getCreditCards(this.id);
}
/**
* Creates a new credit card for this contact.
* @param data - The data to create the credit card with.
* @returns The newly created credit card if the API call was successful.
*/
addCreditCard(data: object): Promise<CreditCard> {
return this.contacts.createCreditCard(this.id, data);
}
/**
* Fetches the email addresses associated with this contact.
* @param options - The options to use when fetching email addresses.
* @returns The email addresses associated with this contact if the API call was successful.
*/
getEmails(options?: {
email?: string;
limit?: number;
offset?: number;
}): Promise<Paginator<EmailRecord>> {
return this.contacts.listEmails(this.id, options);
}
/**
* Adds a new email address to this contact.
* @param data - The data to create the email address with.
* @returns The newly created email address if the API call was successful.
*/
addEmail(data: EmailRecord): Promise<EmailRecord> {
return this.contacts.createEmail(this.id, data);
}
/**
* Fetches the tags applied to this contact.
* @param options - The options to use when fetching tags.
* @returns The tags applied to this contact if the API call was successful.
*/
getAppliedTags(options?: {
limit?: number;
offset?: number;
}): Promise<Paginator<Tag>> {
return this.contacts.listAppliedTags(this.id, options);
}
/**
* Applies the given tags to this contact.
* @param tagIds - The IDs of the tags to apply.
* @returns The result of the API call if it was successful.
*/
applyTags(tagIds: number[]): Promise<object> {
return this.contacts.applyTags(this.id, tagIds);
}
/**
* Removes the given tag from this contact.
* @param tagId - The ID of the tag to remove.
* @returns The result of the API call if it was successful.
*/
removeTag(tagId: number): Promise<object> {
return this.contacts.removeTag(this.id, tagId);
}
/**
* Removes multiple tags from this contact.
* @param tagIds - The IDs of the tags to remove.
* @param data - Data to be sent with the request.
* @returns The result of the API call if it was successful.
*/
removeTags(tagIds: number[], data: object): Promise<object> {
return this.contacts.removeTags(this.id, tagIds, data);
}
/**
* Adds UTM tracking data to this contact.
* @param utmData - The UTM data to add.
* @returns The result of the API call if it was successful.
*/
addUTM(utmData: UTM): Promise<UTM> {
return this.contacts.addUTM(this.id, utmData);
}
}