keap-client
Version:
A client to work with Infusionsoft/Keap API.
589 lines (548 loc) • 20 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.
* @param parameters - Options for fetching contacts, like page size and page number.
* email:string //Optional email address to query on
* family_name:string //Optional last name or surname to query on
* given_name:string //Optional first name or forename to query on
* limit:number //Sets a total of items to return
* offset:number //Sets a beginning range of items to return
* optional_properties:string[] //Comma-delimited list of Contact properties to include in the response. (Some fields such as lead_source_id, custom_fields, and job_title aren't included, by default.)
* order:string //Enum: "id" "date_created" "last_updated" "name" "firstName" "email" //Attribute to order items by
* order_direction:string //Enum: "ASCENDING" "DESCENDING" //How to order the data i.e. ascending (A-Z) or descending (Z-A)
* since:string //Date to start searching from on LastUpdated ex. 2017-01-01T22:17:59.039Z
* until:string //Date to search to on LastUpdated ex. 2017-01-01T22:17:59.039Z
* @example
* const contacts = await contacts.getContacts({ limit: 5, offset: 10 });
* console.log(contacts);
* @returns The list of contacts if the API call was successful, undefined otherwise.
* @throws Will throw an error if the API call fails.
*/
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> | undefined> {
//! // IMPORTANT use this case as an example of how the paginator works and how the caller method should look
const params = parameters
? createParams(parameters, [
"email",
"limit",
"offset",
"family_name",
"given_name",
"optional_properties",
"order",
"order_direction",
"since",
"until",
])
: "";
const r = await this.api.get(`v1/contacts?${params}`);
// Add this line to assert that r is an object with a 'contacts' property
const rObj = r as { contacts: IContact[] | null };
if (!rObj || !Object.prototype.hasOwnProperty.call(rObj, "contacts")) {
return undefined;
}
if (rObj.contacts === null) {
throw new Error("No contacts found");
}
const contacts = rObj.contacts.map((c: IContact) => {
if (c === null) {
throw new Error("Contact is null");
}
return new Contact(this, c);
});
const paginator: Paginator<IContact> = Paginator.wrap(
this.api,
{ ...r, contacts },
"contacts"
);
return paginator;
}
/**
* Creates a new contact in Keap. NB: Contact must contain at least one item in email_addresses or phone_numbers and country_code is required if region is specified on the adresses.
* @param contactData {IContact} - The data to create a contact with ContactData.
* @returns The newly created contact if the API call was successful, undefined otherwise.
* @example
* const contact = await contacts.createContact(given_name: 'John',
* family_name: 'Doe',
* email_addresses: [
* {
* email: "string@example.com",
* field: "EMAIL1"
* }
* ],);
* console.log(contact);
* @throws Will throw an error if the API call fails.
*/
async createContact(contactData: IContact): Promise<Contact | undefined> {
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 r = await this.api.post("v1/contacts", contactData as IContact);
if (!r) return undefined;
return new Contact(this, r 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, undefined otherwise.
* @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 | undefined> {
const r = await this.api.patch(`v1/contacts/${contactId}`, contactData);
if (!r) return undefined;
return new Contact(this, r as IContact);
}
/**
* Deletes a contact by ID.
* @param contactId - The ID of the contact to delete.
* @returns The result of the API call if it was successful, undefined otherwise.
* @throws Will throw an error if the API call fails.
* @example
* const result = await contacts.deleteContact(123);
* console.log(result); // true
* if (result) {
* console.log("Contact deleted successfully");
* }else{
* console.log("Failed to delete contact");
* }
*/
async deleteContact(contactId: number): Promise<boolean | undefined> {
const r = this.api.delete(`v1/contacts/${contactId}`);
if (!r) return undefined;
return true;
}
/**
* Fetches a single contact by ID.
* @param contactId - The ID of the contact to fetch.
* @returns The contact if the API call was successful, undefined otherwise.
* @throws Will throw an error if the API call fails.
* @example
* const contact = await contacts.getContact(123);
* console.log(contact.given_name); // "Jane"
* console.log(contact.family_name); // "Doe"
*/
async getContact(contactId: number): Promise<Contact | undefined> {
const r = await this.api.get(`v1/contacts/${contactId}`);
if (!r) return undefined;
return new Contact(this, r as IContact);
}
/**
* Fetches the contact model.
* @returns The contact model if the API call was successful, undefined otherwise.
* @throws Will throw an error if the API call fails.
* @example
* const contactModel = await contacts.getContactModel();
* console.log(contactModel);
*/
async getContactModel(): Promise<ContactModel | undefined> {
const r = await this.api.get("v1/contacts/model");
if (!r) return undefined;
return r as Promise<ContactModel>;
}
/**
* Creates a new custom contact field.
* @param customFieldData{CustomField} - The data to create a custom field with.
* @returns The newly created custom field if the API call was successful, undefined otherwise.
* @throws Will throw an error if the API call fails.
*/
async createCustomField(
customFieldData: CustomField
): Promise<CustomField | undefined> {
const r = await this.api.post("v1/contacts/customFields", customFieldData);
if (!r) return undefined;
return r as Promise<CustomField>;
}
/**
* Creates a new contact if the contact does not exist, or updates the contact if it does.
* @param contactData {IContact} - The data to create or update a contact with.
* @returns The newly created or updated contact if the API call was successful, undefined otherwise.
* @throws Will throw an error if the API call fails.
* @example
* const contact = await contacts.createOrUpdate({
* given_name: "Jane",
* family_name: "Doe"
* })
*
* console.log(contact); // {given_name: "Jane", family_name: "Doe", id: 123}
*
*
* const contact = await contacts.createOrUpdate({
* given_name: "John",
* family_name: "Doe"
* id: 123
* })
*
* console.log(contact); // {given_name: "John", family_name: "Doe", id: 123}
*/
async createOrUpdate(contactData: IContact): Promise<Contact | undefined> {
const r = await this.api.put("v1/contacts", contactData);
if (!r) return undefined;
return new Contact(this, r 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, undefined otherwise.
* @throws Will throw an error if the API call fails.
*/
getCreditCards(contactId: number): Promise<object | undefined> {
return this.api.get(`v1/contacts/${contactId}/creditCards`);
}
/**
* 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 {Promise<CreditCard>} - The newly created credit card if the API call was successful, undefined otherwise.
*/
createCreditCard(
contactId: number,
creditCardData: object
): Promise<CreditCard | undefined> {
return this.api.post(
`v1/contacts/${contactId}/creditCards`,
creditCardData
) as Promise<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, undefined otherwise.
*/
async listEmails(
contactId: number,
options?: { email?: string; limit?: number; offset?: number }
): Promise<Paginator<EmailRecord> | undefined> {
const params = options
? createParams(options, ["email", "limit", "offset"])
: "";
const r = await this.api.get(
`v1/contacts/${contactId}/emails?${params?.toString()}`
);
if (!r) return undefined;
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, undefined otherwise.
*/
createEmail(
contactId: number,
emailData: EmailRecord
): Promise<EmailRecord | undefined> {
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, undefined otherwise.
*/
async listAppliedTags(
contactId: number,
options?: { limit?: number; offset?: number }
): Promise<Paginator<Tag> | undefined> {
const params = options ? createParams(options, ["limit", "offset"]) : "";
const r = await this.api.get(
`v1/contacts/${contactId}/tags?${params?.toString()}`
);
if (!r) return undefined;
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, undefined otherwise.
*/
applyTags(contactId: number, tagIds: number[]): Promise<object | undefined> {
return this.api.post(`v1/contacts/${contactId}/tags`, {
tagIds,
});
}
/**
* 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, undefined otherwise.
*/
removeTag(contactId: number, tagId: number): Promise<object | undefined> {
return this.api.delete(`v1/contacts/${contactId}/tags/${tagId}`);
}
/**
* 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, undefined otherwise.
*/
removeTags(
contactId: number,
tagIds: number[],
data: object
): Promise<object | undefined> {
return this.api.delete(
`v1/contacts/${contactId}/tags?tagIds=${tagIds.join(",")}`,
data
);
}
/**
* 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, undefined otherwise.
*/
addUTM(contactId: number, utmData: UTM): Promise<UTM | undefined> {
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, undefined otherwise.
*/
async update(data: IContact): Promise<Contact | undefined> {
const r = await this.contacts.updateContact(this.id, data);
if (!r) return undefined;
return new Contact(this.contacts, r);
}
/**
* Deletes the contact.
* @returns The result of the API call if it was successful, undefined otherwise.
* @throws Will throw an error if the API call fails.
*/
async delete(): Promise<boolean | undefined> {
return this.contacts.deleteContact(this.id);
}
/**
* Refreshes the contact data from the API.
* @returns The updated contact if the API call was successful, undefined otherwise.
*/
async refresh(): Promise<Contact | undefined> {
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, undefined otherwise.
*/
getCreditCards(): Promise<object | undefined> {
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, undefined otherwise.
*/
addCreditCard(data: object): Promise<CreditCard | undefined> {
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, undefined otherwise.
*/
getEmails(options?: {
email?: string;
limit?: number;
offset?: number;
}): Promise<Paginator<EmailRecord> | undefined> {
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, undefined otherwise.
*/
addEmail(data: EmailRecord): Promise<EmailRecord | undefined> {
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, undefined otherwise.
*/
getAppliedTags(options?: {
limit?: number;
offset?: number;
}): Promise<Paginator<Tag> | undefined> {
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, undefined otherwise.
*/
applyTags(tagIds: number[]): Promise<object | undefined> {
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, undefined otherwise.
*/
removeTag(tagId: number): Promise<object | undefined> {
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, undefined otherwise.
*/
removeTags(tagIds: number[], data: object): Promise<object | undefined> {
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, undefined otherwise.
*/
addUTM(utmData: UTM): Promise<UTM | undefined> {
return this.contacts.addUTM(this.id, utmData);
}
}