Skip to content

Commit e422ab4

Browse files
committed
Updated source grouping to use known referrers data
refs TryGhost/Team#1923 - updates referrer translator logic to use grouping for known domains/urls to calculate source and medium - returns first external ref url if nothing matches in history in first pass
1 parent 4ebe94f commit e422ab4

File tree

3 files changed

+104
-7
lines changed

3 files changed

+104
-7
lines changed

ghost/member-attribution/lib/referrer-translator.js

+28-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* @prop {URL|null} [refUrl]
66
*/
77

8+
const knownReferrers = require('@tryghost/referrers');
9+
810
/**
911
* Translates referrer info into Source and Medium
1012
*/
@@ -64,7 +66,7 @@ class ReferrerTranslator {
6466

6567
// If referrer is from query params
6668
if (refSource) {
67-
const urlData = this.getDataFromUrl() || {};
69+
const urlData = refUrl ? this.getDataFromUrl(refUrl) : null;
6870
return {
6971
refSource: refSource,
7072
refMedium: refMedium || urlData?.medium || null,
@@ -73,9 +75,8 @@ class ReferrerTranslator {
7375
}
7476

7577
// If referrer is known external URL
76-
// TODO: Use list of known external urls to fetch source/medium
7778
if (refUrl && !this.isSiteDomain(refUrl)) {
78-
const urlData = this.getDataFromUrl();
79+
const urlData = this.getDataFromUrl(refUrl);
7980

8081
if (urlData) {
8182
return {
@@ -87,13 +88,34 @@ class ReferrerTranslator {
8788
}
8889
}
8990

91+
// Return any referrer URL in history that is not site domain
92+
for (const item of history) {
93+
const refUrl = this.getUrlFromStr(item.refUrl);
94+
95+
if (refUrl && !this.isSiteDomain(refUrl)) {
96+
return {
97+
refSource: null,
98+
refMedium: null,
99+
refUrl: refUrl
100+
};
101+
}
102+
}
103+
90104
return null;
91105
}
92106

93107
// Fetches referrer data from known external URLs
94-
//TODO: Use list of known external urls to fetch source/medium
95-
getDataFromUrl() {
96-
return null;
108+
getDataFromUrl(url) {
109+
// Allow matching both "google.ac/products" and "google.ac" as a source
110+
const urlHostPath = url?.host + url?.pathname;
111+
const urlDataKey = Object.keys(knownReferrers).sort((a, b) => {
112+
// The longer key has higher the priority so google.ac/products is selected before google.ac
113+
return b.length - a.length;
114+
}).find((source) => {
115+
return urlHostPath?.startsWith(source);
116+
});
117+
118+
return urlDataKey ? knownReferrers[urlDataKey] : null;
97119
}
98120

99121
/**

ghost/member-attribution/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
},
2222
"dependencies": {
2323
"@tryghost/domain-events": "0.0.0",
24-
"@tryghost/member-events": "0.0.0"
24+
"@tryghost/member-events": "0.0.0",
25+
"@tryghost/referrers": "0.0.0"
2526
}
2627
}

ghost/member-attribution/test/referrer-translator.test.js

+74
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,80 @@ describe('ReferrerTranslator', function () {
163163
});
164164
});
165165

166+
describe('returns source and medium for', function () {
167+
it('known external url with path', async function () {
168+
should(translator.getReferrerDetails([
169+
{
170+
refSource: null,
171+
refMedium: null,
172+
refUrl: 'https://google.ac/products'
173+
},
174+
{
175+
refSource: null,
176+
refMedium: null,
177+
refUrl: 'https://t.co/'
178+
},
179+
{
180+
refSource: 'publisher-weekly-newsletter',
181+
refMedium: null,
182+
refUrl: null
183+
},
184+
{
185+
refSource: 'ghost-explore',
186+
refMedium: null,
187+
refUrl: null
188+
}
189+
])).eql({
190+
refSource: 'Google Product Search',
191+
refMedium: 'search',
192+
refUrl: new URL('https://google.ac/products')
193+
});
194+
});
195+
196+
it('known external url without path', async function () {
197+
should(translator.getReferrerDetails([
198+
{
199+
refSource: null,
200+
refMedium: null,
201+
refUrl: 'https://t.co/'
202+
},
203+
{
204+
refSource: 'publisher-weekly-newsletter',
205+
refMedium: null,
206+
refUrl: null
207+
},
208+
{
209+
refSource: 'ghost-explore',
210+
refMedium: null,
211+
refUrl: null
212+
}
213+
])).eql({
214+
refSource: 'Twitter',
215+
refMedium: 'social',
216+
refUrl: new URL('https://t.co/')
217+
});
218+
});
219+
});
220+
221+
it('returns external ref url if nothing matches', async function () {
222+
should(translator.getReferrerDetails([
223+
{
224+
refSource: null,
225+
refMedium: null,
226+
refUrl: 'https://example.com'
227+
},
228+
{
229+
refSource: null,
230+
refMedium: null,
231+
refUrl: 'https://sample.com'
232+
}
233+
])).eql({
234+
refSource: null,
235+
refMedium: null,
236+
refUrl: new URL('https://sample.com')
237+
});
238+
});
239+
166240
it('returns null for empty history', async function () {
167241
should(translator.getReferrerDetails([])).eql(null);
168242
});

0 commit comments

Comments
 (0)