@vivianhealth/braze-segment-debounce
Version:
Segment utilities to debounce data before it reaches the Braze destination
188 lines (156 loc) • 7.48 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.debouncePayload = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _index = require("./index");
var _utils = require("./utils");
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
/**
* Default serialize function for the server
* @param {object|array|null|string|number} value
* @return {Buffer}
*/
var serializeAsJSONForCache = function serializeAsJSONForCache(value) {
return Buffer.from(JSON.stringify(value), 'utf8');
};
/**
* Default deserialize function for frontend
* @param {Buffer} serializedValue
* @return {object|array|null|string|number}
*/
var deserializeFromJSONForCache = function deserializeFromJSONForCache(serializedValue) {
return JSON.parse(serializedValue.toString('utf8'));
};
/*
* `debouncePayload` is meant to work with `anayltics-node`. It does not
* require middleware like the frontend but it does rely on a caching
* or storage mechanism that the user provides. Similar to
* `debouncePayloadSync`, it stores previous payloads using the storage
* mechanism, and compares the `payload` to be sent to Segment/Braze
* against the previous versions so that it only sends new or updated
* `traits`.
* @param {Object} payload The data to be sent to Segment/Braze
* @param {Function} fetchPayload A function to retrieve previous payloads
* @param {Function} persistPayload A function to store previous payloads
* @param {Function} options.serializePayload An optional function to
* override the default serializer
* @param {Function} options.deserializePayload An optional function to
* override the default deserialize
* @return {Object} The debounced payload to send to Braze. If `null`,
* there is no new or updated data to send.
* @example
* ```
* import _isNil from 'lodash/isNil';
* import Analytics from 'analytics-node';
* import memjs from 'memjs';
* import { debouncePayload } from 'braze-segment-debounce/server';
*
* const segmentWriteKey = 'YOUR-SEGMENT-WRITE-KEY';
* const analytics = new Analytics(segmentWriteKey);
* const cache = memjs.Client.create();
* const fetchPayload = async (k) => cache.get(k);
* const persistPayload = async (k, v) => {
* await cache.set(k, v, { expires: 3600 });
* };
*
* const identifyWithDebounce = async (payload) => {
* // TODO filter Braze integration, called `AppBoy`, if possible
* const identifyPayload = await debouncePayload(
* payload,
* fetchPayload,
* persistPayload,
* );
* if (!_isNil(identifyPayload)) {
* return analytics.identify(identifyPayload);
* }
* return null;
* };
* ```
*/
var debouncePayload = /*#__PURE__*/function () {
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(payload, fetchPayload, persistPayload) {
var _ref2,
_ref2$serializePayloa,
serializePayload,
_ref2$deserializePayl,
deserializePayload,
getPayloadProperty,
key,
parsedPreviousPayload,
rawPreviousPayload,
previousPayload,
sanitizedPayload,
_debouncePayloads,
nextPayload,
newOrUpdatedTraits,
debouncedPayload,
previousPayloadToStore,
_args = arguments;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_ref2 = _args.length > 3 && _args[3] !== undefined ? _args[3] : {}, _ref2$serializePayloa = _ref2.serializePayload, serializePayload = _ref2$serializePayloa === void 0 ? serializeAsJSONForCache : _ref2$serializePayloa, _ref2$deserializePayl = _ref2.deserializePayload, deserializePayload = _ref2$deserializePayl === void 0 ? deserializeFromJSONForCache : _ref2$deserializePayl;
getPayloadProperty = function getPayloadProperty(payload, prop) {
return payload[prop];
};
key = (0, _utils.getPayloadKey)(payload, getPayloadProperty);
parsedPreviousPayload = null;
_context.prev = 4;
_context.next = 7;
return fetchPayload(key);
case 7:
rawPreviousPayload = _context.sent;
if (rawPreviousPayload) {
parsedPreviousPayload = deserializePayload(rawPreviousPayload);
}
_context.next = 14;
break;
case 11:
_context.prev = 11;
_context.t0 = _context["catch"](4);
console.error('debouncePayload: failed to fetch previous payload for debouncing');
case 14:
previousPayload = parsedPreviousPayload || {}; // Remove undefined fields
sanitizedPayload = deserializePayload(serializePayload(payload));
_debouncePayloads = (0, _index.debouncePayloads)(previousPayload, sanitizedPayload, getPayloadProperty), nextPayload = _debouncePayloads.nextPayload, newOrUpdatedTraits = _debouncePayloads.newOrUpdatedTraits;
if (!nextPayload) {
_context.next = 29;
break;
}
debouncedPayload = _objectSpread(_objectSpread({}, nextPayload), {}, {
traits: newOrUpdatedTraits || nextPayload.traits
});
previousPayloadToStore = (0, _utils.combinePreviousPayloads)([previousPayload, sanitizedPayload]);
_context.prev = 20;
_context.next = 23;
return persistPayload(key, serializePayload(previousPayloadToStore));
case 23:
_context.next = 28;
break;
case 25:
_context.prev = 25;
_context.t1 = _context["catch"](20);
console.error('debouncePayload: failed to store previous payload for debouncing');
case 28:
return _context.abrupt("return", debouncedPayload);
case 29:
return _context.abrupt("return", null);
case 30:
case "end":
return _context.stop();
}
}
}, _callee, null, [[4, 11], [20, 25]]);
}));
return function debouncePayload(_x, _x2, _x3) {
return _ref.apply(this, arguments);
};
}();
exports.debouncePayload = debouncePayload;