facebook-nodejs-business-sdk
Version:
SDK for the Facebook Marketing API in Javascript and Node.js
325 lines (273 loc) • 10.2 kB
JavaScript
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
* @flow
*/
import DeliveryCategory from './delivery-category.js';
const sha256 = require('js-sha256');
const currency_codes = require('currency-codes');
const country_codes = require('iso-3166-1');
const validator = require('email-validator');
const PHONE_NUMBER_IGNORE_CHAR_SET = /[\-@#<>'",; ]|\(|\)|\+|[a-z]/g;
const PHONE_NUMBER_DROP_PREFIX_ZEROS = /^\+?0{0,2}/;
const US_PHONE_NUMBER_REGEX = /^1\(?\d{3}\)?\d{7}$/;
const INTL_PHONE_NUMBER_REGEX = /^\d{1,4}\(?\d{2,3}\)?\d{4,}$/;
const SHA256_REGEX = /^[a-f0-9]{64}$/;
const MD5_REGEX = /^[a-f0-9]{32}$/;
/**
* ServerSideUtils contains the Utility modules used for sending Conversions API Events
*/
export default class ServerSideUtils {
/**
* Normalizes and hashes the input given the field name.
* @param {String} [input] Value to be normalized. eg: `foo@bar.com` for email input.
* @param {String} [field] Key(Type) of Value to be normalized eg: 'em' for email field.
* @return {String} Normalized and hashed value for the string.
*/
static normalizeAndHash (input: string, field: string) {
if (field == null || input == null) {
return null;
}
let normalized_input = input.trim().toLowerCase();
if (normalized_input.length === 0) {
return null;
}
if (normalized_input.match(SHA256_REGEX) || normalized_input.match(MD5_REGEX)) {
return normalized_input;
}
switch (field) {
case 'country':
normalized_input = ServerSideUtils.normalizeCountry(normalized_input);
break;
case 'ct':
normalized_input = ServerSideUtils.normalizeCity(normalized_input);
break;
case 'em':
normalized_input = ServerSideUtils.normalizeEmail(normalized_input);
break;
case 'ge':
normalized_input = ServerSideUtils.normalizeGender(normalized_input);
break;
case 'ph':
normalized_input = ServerSideUtils.normalizePhone(normalized_input);
break;
case 'st':
normalized_input = ServerSideUtils.normalizeState(normalized_input);
break;
case 'zp':
normalized_input = ServerSideUtils.normalizeZip(normalized_input);
break;
case 'f5first':
case 'f5last':
normalized_input = ServerSideUtils.normalizeF5NameField(normalized_input);
break;
case 'fi':
normalized_input = normalized_input.charAt(0);
break;
case 'dobd':
normalized_input = ServerSideUtils.normalizeDobd(normalized_input);
break;
case 'dobm':
normalized_input = ServerSideUtils.normalizeDobm(normalized_input);
break;
case 'doby':
normalized_input = ServerSideUtils.normalizeDoby(normalized_input);
break;
}
// Hashing the normalized input with SHA 256
const hashed_input = ServerSideUtils.toSHA256(normalized_input);
return hashed_input;
}
/**
* Normalizes the given country token and returns acceptable two letter ISO country code
* @param {String} [country] country value to be normalized.
* @return {String} Normalized ISO country code.
*/
static normalizeCountry (country: string) {
if (!country_codes.whereAlpha2(country)) {
throw new Error("Invalid country code: '" + country + "'. Please follow ISO 3166-1 2-letter standard for representing country. eg: US");
}
return country;
}
/**
* Normalizes the given city and returns acceptable city value
* @param {String} [city] city value to be normalized.
* @return {String} Normalized city value.
*/
static normalizeCity (city: string) {
city = city.replace(/[0-9\s().-]/g, '');
return city;
}
/**
* Normalizes the given currency string and returns acceptable three letter ISO code
* @param {String} [currency] Currency value to be normalized.
* @return {String} Normalized ISO currency code.
*/
static normalizeCurrency(currency: string) {
// Convert the input currency string to uppercase
currency = currency.trim().toUpperCase();
// Retain only uppercase alphabetic characters (A-Z) bounded for ISO code
currency = currency.replace(/[^A-Z]/g, '');
// Check if the normalized currency is a valid ISO 4217 code
if (!currency_codes.codes().includes(currency)) {
throw new Error("Invalid format for currency: '" + currency + "'. Please follow ISO 4217 3-letter standard for representing currency. Eg: USD");
}
// Return the normalized, uppercase currency code
return currency;
}
/**
* Normalizes the given delivery category value and returns a valid string.
* @param {String} [input] delivery_category input to be validated.
* @return {String} Valid delivery_category value.
*/
static normalizeDeliveryCategory(input: string) {
let delivery_category = input.trim().toLowerCase();
if(!(Object.values(DeliveryCategory).includes(delivery_category))){
throw new Error("Invalid delivery_category passed: " + input +
". Allowed values are one of " + (Object.values(DeliveryCategory)).join(','));
}
return delivery_category;
}
/**
* @param {String} [email] email value to be normalized.
* @return {String} Normalized email value.
*/
static normalizeEmail (email: string) {
// Use email-validator to validate the email format
if (!validator.validate(email)) {
throw new Error("Invalid email format for the passed email:'" + email + "'.Please check the passed email format.");
}
return email;
}
/**
* Normalizes the given gender and returns acceptable('f' or 'm') gender value
* @param {String} [gender] gender value to be normalized.
* @return {String} Normalized gender value.
*/
static normalizeGender (gender: string) {
gender = gender.replace(/[^a-z]/g, '');
if (gender === 'female' || gender === 'f') {
gender = 'f';
} else if (gender === 'male' || gender === 'm') {
gender = 'm';
} else { return null; }
return gender;
}
/**
* Normalizes the 5 character name field.
* @param {String} [name] name value to be normalized.
* @return {String} Normalized 5 character {first,last}name field value.
*/
static normalizeF5NameField (name: string) {
return name.length <= 5 ? name : name.substring(0,5);
}
/**
* Normalizes the given phone and returns acceptable phone value
* @param {String} [phone_number] phone number value to be normalized.
* @return {String} Normalized phone number value.
*/
static normalizePhone (phone_number: string) {
// Remove common characters occuring as part of the phone numbers.
phone_number = phone_number.replace(PHONE_NUMBER_IGNORE_CHAR_SET, '');
if (ServerSideUtils.isInternationalPhoneNumber(phone_number)) {
phone_number = phone_number.replace(PHONE_NUMBER_DROP_PREFIX_ZEROS, '');
}
if (phone_number.length < 7 || phone_number.length > 16) {
throw new Error("Invalid phone number format for the passed phone number:'" + phone_number + "'.Please check the passed phone number format.");
}
return phone_number;
}
/**
* Normalizes the given state and returns acceptable city value
* @param {String} [state] state value to be normalized.
* @return {String} Normalized state value.
*/
static normalizeState (state: string) {
state = state.replace(/[0-9\s().-]/g, '');
return state;
}
/**
* Normalizes the given zip/postal code and returns acceptable zip code value
* @param {String} [zip] zip value to be normalized.
* @return {String} Normalized zip code value.
*/
static normalizeZip (zip: string) {
zip = zip.replace(/[\s]/g, '');
// If the zip code '-', we retain just the first part alone.
zip = zip.split('-', 1)[0];
if (zip.length < 2) {
return null;
}
return zip;
}
/**
* Normalizes the given date of birth day
* @param {String} [dobd] value to be normalized.
* @return {String} Normalized value.
*/
static normalizeDobd (dobd: string) {
if (dobd.length === 1) {
dobd = '0' + dobd;
}
const dobd_int = parseInt(dobd);
if (dobd_int < 1 || dobd_int > 31) {
throw new Error("Invalid format for dobd:'" + dobd + "'.Please use 'DD' format for dobd.")
}
return dobd;
}
/**
* Normalizes the given date of birth month
* @param {String} [dobm] value to be normalized.
* @return {String} Normalized value.
*/
static normalizeDobm (dobm: string) {
if (dobm.length === 1) {
dobm = '0' + dobm;
}
const dobm_int = parseInt(dobm);
if (dobm_int < 1 || dobm_int > 12) {
throw new Error("Invalid format for dobm:'" + dobm + "'.Please use 'MM' format for dobm.")
}
return dobm;
}
/**
* Normalizes the given date of birth year
* @param {String} [doby] value to be normalized.
* @return {String} Normalized value.
*/
static normalizeDoby (doby: string) {
if (!doby.match(/^[0-9]{4}$/)) {
throw new Error("Invalid format for doby:'" + doby + "'.Please use 'YYYY' format for doby.")
}
return doby;
}
/**
* Boolean method which checks if a given number is represented in international format
* @param {String} phone_number that has to be tested.
* @return {Boolean} value if a number is represented international format
*/
static isInternationalPhoneNumber (phone_number: string) {
// strip up to 2 leading 0s and +
phone_number = phone_number.replace(PHONE_NUMBER_DROP_PREFIX_ZEROS, '');
if (phone_number.startsWith('0')) {
return false;
}
if (phone_number.startsWith('1')) {
return US_PHONE_NUMBER_REGEX.test(phone_number);
}
return INTL_PHONE_NUMBER_REGEX.test(phone_number);
}
/**
* Calculates the SHA 256 hash of a given non-null string.
* @param {String} [input] String to be hashed
* @return {String} SHA 256 Hash of the string
*/
static toSHA256(input: ?string) {
if (input === null)
return input;
return sha256(input);
}
}