Ghost/ghost/member-attribution/lib/UrlHistory.js
Simon Backx 7e27d3f3e8
Added signup form attribution (#16899)
fixes https://github.com/TryGhost/Team/issues/3331

This adds attribution tracking to the signup form. It sends a newly
created url history when sending the signup API call, this url history
will get translated to a proper attribution and saved on the backend. We
send a history with only a single item that contains the referrer
source, medium and path of the Embed form.

This also makes some changes to the E2E tests so that the tests run
in an https environment instead of about:blank.
2023-06-01 10:18:11 +02:00

93 lines
2.1 KiB
JavaScript

/**
* @typedef {Object} UrlHistoryItem
* @prop {string} [path]
* @prop {string} [id]
* @prop {string} [type]
* @prop {string} [referrerSource]
* @prop {string} [referrerMedium]
* @prop {string} [referrerUrl]
* @prop {number} time
*/
/**
* @typedef {UrlHistoryItem[]} UrlHistoryArray
*/
/**
* Types allowed to add in the URLHistory manually
*/
const ALLOWED_TYPES = ['post'];
/**
* Represents a validated history
*/
class UrlHistory {
/**
* @private
* @param {UrlHistoryArray} urlHistory
*/
constructor(urlHistory) {
/** @private */
this.history = urlHistory;
}
get length() {
return this.history.length;
}
/**
* Iterate from latest item to newest item (reversed!)
*/
*[Symbol.iterator]() {
yield* this.history.slice().reverse();
}
/**
* @private
* @param {any[]} history
* @returns {history is UrlHistoryArray}
*/
static isValidHistory(history) {
for (const item of history) {
const isValidIdEntry = typeof item?.id === 'string' && typeof item?.type === 'string' && ALLOWED_TYPES.includes(item.type);
const isValidPathEntry = typeof item?.path === 'string';
const isValidReferrerSource = typeof item?.referrerSource === 'string';
const isValidEntry = isValidPathEntry || isValidIdEntry || isValidReferrerSource;
if (!isValidEntry || !Number.isSafeInteger(item?.time)) {
return false;
}
}
return true;
}
/**
* @param {unknown} urlHistory
* @returns {UrlHistory}
*/
static create(urlHistory) {
if (!Array.isArray(urlHistory)) {
return new UrlHistory([]);
}
if (!this.isValidHistory(urlHistory)) {
return new UrlHistory([]);
}
const now = Date.now();
const filteredHistory = urlHistory.filter((item) => {
return now - item.time < this.MAX_AGE;
});
return new UrlHistory(filteredHistory);
}
/**
* @private
*/
static MAX_AGE = 1000 * 60 * 60 * 24;
}
module.exports = UrlHistory;