UNPKG

keap-client

Version:

A client to work with Infusionsoft/Keap API.

589 lines (548 loc) 20 kB
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); } }