Revert "Added member attributions to activity feed (#15283)"

This reverts commit e986b78458.

The tests were not passing for the PR and it was erroneously
merged into main
This commit is contained in:
Fabien "egg" O'Carroll 2022-08-24 11:01:47 -04:00
parent fce4b26601
commit 3c431bd8da
23 changed files with 1761 additions and 3352 deletions

View File

@ -102,8 +102,9 @@
<span class="gh-dashboard-list-subtext">
{{capitalize-first-letter parsedEvent.action}}
{{#if parsedEvent.url}}
{{parsedEvent.join}}
<a class="ghost-members-activity-object-link {{if (feature "memberAttribution") 'hidden'}}" href="{{parsedEvent.url}}" target="_blank" rel="noopener noreferrer">{{parsedEvent.object}}</a>
{{else}}
{{parsedEvent.object}}
{{/if}}
{{#if parsedEvent.info}}
<span class="highlight">{{parsedEvent.info}}</span>

View File

@ -27,8 +27,9 @@
<span class="gh-members-activity-description">
{{capitalize-first-letter event.action}}
{{#if event.url}}
{{event.join}}
<a class="ghost-members-activity-object-link" href="{{event.url}}" target="_blank" rel="noopener noreferrer">{{event.object}}</a>
{{else}}
{{event.object}}
{{/if}}
{{#if event.email}}
<GhEmailPreviewLink @data={{event.email}} />

View File

@ -6,7 +6,7 @@
<div class="flex items-center">
<GhMemberAvatar @member={{event.member}} @containerClass="w9 h9 mr3 flex-shrink-0" />
<div class="w-80">
<h3 class="ma0 pa0 gh-members-list-name {{unless event.member.name "gh-members-name-noname"}}">{{event.subject}}</h3>
<h3 class="ma0 pa0 gh-members-list-name {{unless event.member.name "gh-members-name-noname"}}">{{or event.member.name event.member.email}}</h3>
{{#if event.member.name}}
<p class="ma0 pa0 middarkgrey f8 gh-members-list-email">{{event.member.email}}</p>
{{/if}}
@ -21,9 +21,10 @@
<div class="gh-members-activity-event">
<span class="gh-members-activity-description">
{{capitalize-first-letter event.action}}
{{#if (and event.url (not (feature "memberAttribution")))}}
{{event.join}}
<a class="ghost-members-activity-object-link" href="{{event.url}}" target="_blank" rel="noopener noreferrer">{{event.object}}</a>
{{#if event.url}}
<a class="ghost-members-activity-object-link {{if (feature "memberAttribution") 'hidden'}}" href="{{event.url}}" target="_blank" rel="noopener noreferrer">{{event.object}}</a>
{{else}}
{{event.object}}
{{/if}}
{{#if event.email}}
<span class="{{if (feature "memberAttribution") 'hidden'}}"><GhEmailPreviewLink @data={{event.email}} /></span>

View File

@ -2,15 +2,13 @@ import moment from 'moment';
import {getNonDecimal, getSymbol} from 'ghost-admin/utils/currency';
export default function parseMemberEvent(event, hasMultipleNewsletters) {
const subject = event.data.member.name || event.data.member.email;
const icon = getIcon(event);
const action = getAction(event, hasMultipleNewsletters);
const info = getInfo(event);
const join = getJoin(event);
const object = getObject(event);
let subject = event.data.member.name || event.data.member.email;
let icon = getIcon(event);
let action = getAction(event);
let object = getObject(event, hasMultipleNewsletters);
let info = getInfo(event);
const url = getURL(event);
const timestamp = moment(event.data.created_at);
let timestamp = moment(event.data.created_at);
return {
memberId: event.data.member_id ?? event.data.member?.id,
@ -20,7 +18,6 @@ export default function parseMemberEvent(event, hasMultipleNewsletters) {
icon,
subject,
action,
join,
object,
info,
url,
@ -80,7 +77,7 @@ function getIcon(event) {
return 'event-' + icon;
}
function getAction(event, hasMultipleNewsletters) {
function getAction(event) {
if (event.type === 'signup_event') {
return 'signed up';
}
@ -94,98 +91,78 @@ function getAction(event, hasMultipleNewsletters) {
}
if (event.type === 'newsletter_event') {
let newsletter = 'newsletter';
if (hasMultipleNewsletters && event.data.newsletter && event.data.newsletter.name) {
newsletter = 'newsletter ' + event.data.newsletter.name;
}
if (event.data.subscribed) {
return 'subscribed to ' + newsletter;
return 'subscribed to';
} else {
return 'unsubscribed from ' + newsletter;
return 'unsubscribed from';
}
}
if (event.type === 'subscription_event') {
if (event.data.type === 'created') {
return 'started their subscription';
return 'started';
}
if (event.data.type === 'updated') {
return 'changed their subscription';
return 'changed';
}
if (event.data.type === 'canceled') {
return 'canceled their subscription';
return 'canceled';
}
if (event.data.type === 'reactivated') {
return 'reactivated their subscription';
return 'reactivated';
}
if (event.data.type === 'expired') {
return 'ended their subscription';
return 'ended';
}
return 'changed their subscription';
return 'changed';
}
if (event.type === 'email_opened_event') {
return 'opened an email';
return 'opened';
}
if (event.type === 'email_delivered_event') {
return 'received an email';
return 'received';
}
if (event.type === 'email_failed_event') {
return 'failed to receive an email';
return 'failed to receive';
}
if (event.type === 'comment_event') {
if (event.data.parent) {
return 'replied to a comment';
return 'replied to a comment on';
}
return 'commented';
return 'commented on';
}
}
/**
* When we need to append the action and object in one sentence, you can add extra words here.
* E.g.,
* action: 'Signed up'.
* object: 'My blog post'
* When both words need to get appended, we'll add 'on'
* -> do this by returning 'on' in getJoin()
* This string is not added when action and object are in a separete table column, or when the getObject/getURL is empty
*/
function getJoin(event) {
if (event.type === 'signup_event' || event.type === 'subscription_event') {
if (event.data.attribution?.title) {
// Add 'Attributed to ' for now, until this is incorporated in the design
return 'on';
function getObject(event, hasMultipleNewsletters) {
if (event.type === 'newsletter_event') {
if (hasMultipleNewsletters && event.data.newsletter && event.data.newsletter.name) {
return 'newsletter ' + event.data.newsletter.name;
}
return 'newsletter';
}
if (event.type === 'subscription_event') {
return 'their subscription';
}
if (event.type.match?.(/^email_/)) {
return 'an email';
}
if (event.type === 'subscription_event') {
return 'their subscription';
}
if (event.type === 'comment_event') {
if (event.data.post) {
return 'on';
}
}
return '';
}
/**
* Clickable object, shown between action and info, or in a separate column in some views
*/
function getObject(event) {
if (event.type === 'signup_event' || event.type === 'subscription_event') {
if (event.data.attribution?.title) {
// Add 'Attributed to ' for now, until this is incorporated in the design
return event.data.attribution.title;
}
}
if (event.type === 'comment_event') {
if (event.data.post) {
return event.data.post.title;
if (event.type === 'comment_event') {
if (event.data.post) {
return event.data.post.title;
}
}
}
@ -202,6 +179,13 @@ function getInfo(event) {
let symbol = getSymbol(event.data.currency);
return `(MRR ${sign}${symbol}${Math.abs(mrrDelta)})`;
}
// TODO: we can include the post title
/*if (event.type === 'comment_event') {
if (event.data.post) {
return event.data.post.title;
}
}*/
return;
}
@ -214,11 +198,5 @@ function getURL(event) {
return event.data.post.url;
}
}
if (event.type === 'signup_event' || event.type === 'subscription_event') {
if (event.data.attribution && event.data.attribution.url) {
return event.data.attribution.url;
}
}
return;
}

View File

@ -8,16 +8,8 @@ const MemberCreatedEvent = ghostBookshelf.Model.extend({
return this.belongsTo('Member', 'member_id', 'id');
},
postAttribution() {
return this.belongsTo('Post', 'attribution_id', 'id');
},
userAttribution() {
return this.belongsTo('User', 'attribution_id', 'id');
},
tagAttribution() {
return this.belongsTo('Tag', 'attribution_id', 'id');
attribution() {
return this.belongsTo('Post', 'attribution_id', 'id');
}
}, {
async edit() {

View File

@ -8,10 +8,6 @@ const MemberPaidSubscriptionEvent = ghostBookshelf.Model.extend({
return this.belongsTo('Member', 'member_id', 'id');
},
subscriptionCreatedEvent() {
return this.belongsTo('SubscriptionCreatedEvent', 'subscription_id', 'subscription_id');
},
customQuery(qb, options) {
if (options.aggregateMRRDeltas) {
if (options.limit || options.filter) {

View File

@ -12,16 +12,8 @@ const SubscriptionCreatedEvent = ghostBookshelf.Model.extend({
return this.belongsTo('StripeCustomerSubscription', 'subscription_id', 'id');
},
postAttribution() {
return this.belongsTo('Post', 'attribution_id', 'id');
},
userAttribution() {
return this.belongsTo('User', 'attribution_id', 'id');
},
tagAttribution() {
return this.belongsTo('Tag', 'attribution_id', 'id');
attribution() {
return this.belongsTo('Post', 'attribution_id', 'id');
}
}, {
async edit() {

View File

@ -24,7 +24,7 @@ class MemberAttributionServiceWrapper {
}
});
this.attributionBuilder = new AttributionBuilder({urlTranslator});
const attributionBuilder = new AttributionBuilder({urlTranslator});
// Expose the service
this.service = new MemberAttributionService({
@ -32,7 +32,7 @@ class MemberAttributionServiceWrapper {
MemberCreatedEvent: models.MemberCreatedEvent,
SubscriptionCreatedEvent: models.SubscriptionCreatedEvent
},
attributionBuilder: this.attributionBuilder,
attributionBuilder,
labsService
});

View File

@ -185,8 +185,6 @@ function createApiInstance(config) {
MemberStatusEvent: models.MemberStatusEvent,
MemberProductEvent: models.MemberProductEvent,
MemberAnalyticEvent: models.MemberAnalyticEvent,
MemberCreatedEvent: models.MemberCreatedEvent,
SubscriptionCreatedEvent: models.SubscriptionCreatedEvent,
OfferRedemption: models.OfferRedemption,
Offer: models.Offer,
StripeProduct: models.StripeProduct,

View File

@ -1,279 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Members API - member attribution Can read member attributed to a page 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1ffbe2896088840a6e9",
"title": "This is a static page",
"type": "page",
"url": "http://127.0.0.1:2369/static-page-test/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-page@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API - member attribution Can read member attributed to a page 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1955",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API - member attribution Can read member attributed to a post 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1ffbe2896088840a6df",
"title": "HTML Ipsum",
"type": "post",
"url": "http://127.0.0.1:2369/html-ipsum/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-post@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API - member attribution Can read member attributed to a post 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1938",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API - member attribution Can read member attributed to a tag 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1febe2896088840a6db",
"title": "kitchen sink",
"type": "tag",
"url": "http://127.0.0.1:2369/tag/kitchen-sink/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-tag@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API - member attribution Can read member attributed to a tag 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1944",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API - member attribution Can read member attributed to an author 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "1",
"title": "Joe Bloggs",
"type": "author",
"url": "http://127.0.0.1:2369/author/joe-bloggs/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-author@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API - member attribution Can read member attributed to an author 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1926",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API - member attribution Can read member attributed to an url 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": null,
"title": "/a-static-page/",
"type": "url",
"url": "http://127.0.0.1:2369/a-static-page/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-url@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API - member attribution Can read member attributed to an url 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1922",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API - member attribution Returns sign up attributions in activity feed 1: [body] 1`] = `
Object {
"events": Array [
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
],
}
`;
exports[`Members API - member attribution Returns sign up attributions in activity feed 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "8381",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Add should fail when passing incorrect email_type query parameter 1: [body] 1`] = `
Object {
"errors": Array [
@ -4022,280 +3748,3 @@ Object {
"x-powered-by": "Express",
}
`;
exports[`Members APi - member attribution Can read 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": null,
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member1@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": "Mr Egg",
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members APi - member attribution Can read 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1321",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members APi - member attribution Can read member attributed to a page 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1ffbe2896088840a6e9",
"title": "This is a static page",
"type": "page",
"url": "http://127.0.0.1:2369/static-page-test/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-page@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members APi - member attribution Can read member attributed to a page 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1955",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members APi - member attribution Can read member attributed to a post 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1ffbe2896088840a6df",
"title": "HTML Ipsum",
"type": "post",
"url": "http://127.0.0.1:2369/html-ipsum/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-post@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members APi - member attribution Can read member attributed to a post 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1938",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members APi - member attribution Can read member attributed to a tag 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1febe2896088840a6db",
"title": "kitchen sink",
"type": "tag",
"url": "http://127.0.0.1:2369/tag/kitchen-sink/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-tag@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members APi - member attribution Can read member attributed to a tag 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1944",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members APi - member attribution Can read member attributed to an author 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "1",
"title": "Joe Bloggs",
"type": "author",
"url": "http://127.0.0.1:2369/author/joe-bloggs/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-author@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members APi - member attribution Can read member attributed to an author 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1926",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members APi - member attribution Can read member attributed to an url 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": null,
"title": "/a-static-page/",
"type": "url",
"url": "http://127.0.0.1:2369/a-static-page/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": "member-attributed-to-url@test.com",
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "free",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Array [],
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members APi - member attribution Can read member attributed to an url 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1922",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;

View File

@ -10,10 +10,6 @@ const testUtils = require('../../utils');
const Papa = require('papaparse');
const models = require('../../../core/server/models');
const membersService = require('../../../core/server/services/members');
const memberAttributionService = require('../../../core/server/services/member-attribution');
const urlService = require('../../../core/server/services/url');
const urlUtils = require('../../../core/shared/url-utils');
async function assertMemberEvents({eventType, memberId, asserts}) {
const events = await models[eventType].where('member_id', memberId).fetchAll();
@ -157,227 +153,6 @@ describe('Members API without Stripe', function () {
});
});
// Tests specific for member attribution
describe('Members API - member attribution', function () {
const signupAttributions = [];
before(async function () {
agent = await agentProvider.getAdminAPIAgent();
await fixtureManager.init('posts', 'newsletters', 'members:newsletters', 'comments');
await agent.loginAsOwner();
});
beforeEach(function () {
mockManager.mockStripe();
mockManager.mockMail();
// For some reason it is enabled by default?
mockManager.mockLabsEnabled('memberAttribution');
});
afterEach(function () {
mockManager.restore();
});
it('Can read member attributed to a post', async function () {
const id = fixtureManager.get('posts', 0).id;
const post = await models.Post.where('id', id).fetch({require: true});
// Set the attribution for this member manually
const member = await membersService.api.members.create({
email: 'member-attributed-to-post@test.com',
attribution: memberAttributionService.attributionBuilder.build({
id,
url: '/out-of-date/',
type: 'post'
})
});
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true});
await agent
.get(`/members/${member.id}/`)
.expectStatus(200)
.matchBodySnapshot({
members: new Array(1).fill(memberMatcherShallowIncludes)
})
.matchHeaderSnapshot({
etag: anyEtag
})
.expect(({body}) => {
should(body.members[0].attribution).eql({
id: post.id,
url: absoluteUrl,
type: 'post',
title: post.get('title')
});
signupAttributions.push(body.members[0].attribution);
});
});
it('Can read member attributed to a page', async function () {
const id = fixtureManager.get('posts', 5).id;
const post = await models.Post.where('id', id).fetch({require: true});
// Set the attribution for this member manually
const member = await membersService.api.members.create({
email: 'member-attributed-to-page@test.com',
attribution: memberAttributionService.attributionBuilder.build({
id,
url: '/out-of-date/',
type: 'page'
})
});
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true});
await agent
.get(`/members/${member.id}/`)
.expectStatus(200)
.matchBodySnapshot({
members: new Array(1).fill(memberMatcherShallowIncludes)
})
.matchHeaderSnapshot({
etag: anyEtag
})
.expect(({body}) => {
should(body.members[0].attribution).eql({
id: post.id,
url: absoluteUrl,
type: 'page',
title: post.get('title')
});
signupAttributions.push(body.members[0].attribution);
});
});
it('Can read member attributed to a tag', async function () {
const id = fixtureManager.get('tags', 0).id;
const tag = await models.Tag.where('id', id).fetch({require: true});
// Set the attribution for this member manually
const member = await membersService.api.members.create({
email: 'member-attributed-to-tag@test.com',
attribution: memberAttributionService.attributionBuilder.build({
id,
url: '/out-of-date/',
type: 'tag'
})
});
const absoluteUrl = urlService.getUrlByResourceId(tag.id, {absolute: true});
await agent
.get(`/members/${member.id}/`)
.expectStatus(200)
.matchBodySnapshot({
members: new Array(1).fill(memberMatcherShallowIncludes)
})
.matchHeaderSnapshot({
etag: anyEtag
})
.expect(({body}) => {
should(body.members[0].attribution).eql({
id: tag.id,
url: absoluteUrl,
type: 'tag',
title: tag.get('name')
});
signupAttributions.push(body.members[0].attribution);
});
});
it('Can read member attributed to an author', async function () {
const id = fixtureManager.get('users', 0).id;
const author = await models.User.where('id', id).fetch({require: true});
// Set the attribution for this member manually
const member = await membersService.api.members.create({
email: 'member-attributed-to-author@test.com',
attribution: memberAttributionService.attributionBuilder.build({
id,
url: '/out-of-date/',
type: 'author'
})
});
const absoluteUrl = urlService.getUrlByResourceId(author.id, {absolute: true});
await agent
.get(`/members/${member.id}/`)
.expectStatus(200)
.matchBodySnapshot({
members: new Array(1).fill(memberMatcherShallowIncludes)
})
.matchHeaderSnapshot({
etag: anyEtag
})
.expect(({body}) => {
should(body.members[0].attribution).eql({
id: author.id,
url: absoluteUrl,
type: 'author',
title: author.get('name')
});
signupAttributions.push(body.members[0].attribution);
});
});
it('Can read member attributed to an url', async function () {
// Set the attribution for this member manually
const member = await membersService.api.members.create({
email: 'member-attributed-to-url@test.com',
attribution: memberAttributionService.attributionBuilder.build({
id: null,
url: '/a-static-page/',
type: 'url'
})
});
const absoluteUrl = urlUtils.createUrl('/a-static-page/', true);
await agent
.get(`/members/${member.id}/`)
.expectStatus(200)
.matchBodySnapshot({
members: new Array(1).fill(memberMatcherShallowIncludes)
})
.matchHeaderSnapshot({
etag: anyEtag
})
.expect(({body}) => {
should(body.members[0].attribution).eql({
id: null,
url: absoluteUrl,
type: 'url',
title: '/a-static-page/'
});
signupAttributions.push(body.members[0].attribution);
});
});
// Activity feed
it('Returns sign up attributions in activity feed', async function () {
// Check activity feed
await agent
.get(`/members/events/?filter=type:signup_event`)
.expectStatus(200)
.matchHeaderSnapshot({
etag: anyEtag
})
.matchBodySnapshot({
events: new Array(signupAttributions.length).fill({
type: anyString,
data: anyObject
})
})
.expect(({body}) => {
should(body.events.find(e => e.type !== 'signup_event')).be.undefined();
should(body.events.map(e => e.data.attribution)).containDeep(signupAttributions);
});
});
});
describe('Members API', function () {
let newsletters;

View File

@ -1,448 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with author attribution 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "1",
"title": "Joe Bloggs",
"type": "author",
"url": "http://127.0.0.1:2369/author/joe-bloggs/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": Any<String>,
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "paid",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Any<Array>,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with author attribution 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "2795",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with deleted post attribution 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": null,
"title": "/removed-blog-post/",
"type": "url",
"url": "http://127.0.0.1:2369/removed-blog-post/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": Any<String>,
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "paid",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Any<Array>,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with deleted post attribution 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "2809",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with empty attribution object 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": null,
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": Any<String>,
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "paid",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Any<Array>,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with empty attribution object 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "2611",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with page attribution 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1ffbe2896088840a6e9",
"title": "This is a static page",
"type": "page",
"url": "http://127.0.0.1:2369/static-page-test/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": Any<String>,
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "paid",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Any<Array>,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with page attribution 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "2857",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with post attribution 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1ffbe2896088840a6df",
"title": "HTML Ipsum",
"type": "post",
"url": "http://127.0.0.1:2369/html-ipsum/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": Any<String>,
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "paid",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Any<Array>,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with post attribution 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "2823",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with tag attribution 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": "618ba1febe2896088840a6db",
"title": "kitchen sink",
"type": "tag",
"url": "http://127.0.0.1:2369/tag/kitchen-sink/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": Any<String>,
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "paid",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Any<Array>,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with tag attribution 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "2837",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with url attribution 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": Object {
"id": null,
"title": "/",
"type": "url",
"url": "http://127.0.0.1:2369/",
},
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": Any<String>,
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "paid",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Any<Array>,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with url attribution 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "2737",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent without attribution 1: [body] 1`] = `
Object {
"members": Array [
Object {
"attribution": null,
"avatar_image": null,
"comped": false,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"email": Any<String>,
"email_count": 0,
"email_open_rate": null,
"email_opened_count": 0,
"geolocation": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"labels": Any<Array>,
"last_seen_at": null,
"name": null,
"newsletters": Any<Array>,
"note": null,
"status": "paid",
"subscribed": true,
"subscriptions": Any<Array>,
"tiers": Any<Array>,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
],
}
`;
exports[`Members API Member attribution Creates a SubscriptionCreatedEvent without attribution 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "2611",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Returns subscription created attributions in activity feed 1: [body] 1`] = `
Object {
"events": Array [
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
Object {
"data": Any<Object>,
"type": Any<String>,
},
],
}
`;
exports[`Members API Member attribution Returns subscription created attributions in activity feed 1: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "7784",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution Returns subscription created attributions in activity feed 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "13390",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Members API Member attribution empty initial activity feed 1: [body] 1`] = `
Object {
"events": Array [],
}
`;
exports[`Members API Member attribution empty initial activity feed 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "13",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,7 @@ describe('Member Attribution Service', function () {
type: 'url'
}));
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: null,
url: absoluteUrl,
type: 'url',
@ -60,7 +60,7 @@ describe('Member Attribution Service', function () {
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: post.id,
url: absoluteUrl,
type: 'post',
@ -92,7 +92,7 @@ describe('Member Attribution Service', function () {
// Unpublish this post
await models.Post.edit({status: 'draft'}, {id});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: null,
url: absoluteUrl,
type: 'url',
@ -123,7 +123,7 @@ describe('Member Attribution Service', function () {
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: post.id,
url: absoluteUrl,
type: 'page',
@ -150,7 +150,7 @@ describe('Member Attribution Service', function () {
const absoluteUrl = urlService.getUrlByResourceId(tag.id, {absolute: true, withSubdirectory: true});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: tag.id,
url: absoluteUrl,
type: 'tag',
@ -177,7 +177,7 @@ describe('Member Attribution Service', function () {
const absoluteUrl = urlService.getUrlByResourceId(author.id, {absolute: true, withSubdirectory: true});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: author.id,
url: absoluteUrl,
type: 'author',
@ -212,7 +212,7 @@ describe('Member Attribution Service', function () {
type: 'url'
}));
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: null,
url: absoluteUrl,
type: 'url',
@ -245,7 +245,7 @@ describe('Member Attribution Service', function () {
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: post.id,
url: absoluteUrl,
type: 'post',
@ -280,7 +280,7 @@ describe('Member Attribution Service', function () {
// Unpublish this post
await models.Post.edit({status: 'draft'}, {id});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: null,
url: absoluteUrl,
type: 'url',
@ -310,7 +310,7 @@ describe('Member Attribution Service', function () {
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: post.id,
url: absoluteUrl,
type: 'page',
@ -338,7 +338,7 @@ describe('Member Attribution Service', function () {
const absoluteUrl = urlService.getUrlByResourceId(tag.id, {absolute: true, withSubdirectory: true});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: tag.id,
url: absoluteUrl,
type: 'tag',
@ -366,7 +366,7 @@ describe('Member Attribution Service', function () {
const absoluteUrl = urlService.getUrlByResourceId(author.id, {absolute: true, withSubdirectory: true});
(await attribution.fetchResource()).should.match(({
(await attribution.getResource()).should.match(({
id: author.id,
url: absoluteUrl,
type: 'author',

View File

@ -478,27 +478,6 @@ const fixtures = {
return models.Product.add(archivedProduct, context.internal);
},
insertProducts: async function insertProducts() {
let coreProductFixtures = fixtureManager.findModelFixtures('Product').entries;
await Promise.map(coreProductFixtures, async (product) => {
const found = await models.Product.findOne(product, context.internal);
if (!found) {
await models.Product.add(product, context.internal);
}
});
const product = await models.Product.findOne({type: 'paid'}, context.internal);
await Promise.each(_.cloneDeep(DataGenerator.forKnex.stripe_products), function (stripeProduct) {
stripeProduct.product_id = product.id;
return models.StripeProduct.add(stripeProduct, context.internal);
});
await Promise.each(_.cloneDeep(DataGenerator.forKnex.stripe_prices), function (stripePrice) {
return models.StripePrice.add(stripePrice, context.internal);
});
},
insertMembersAndLabelsAndProducts: function insertMembersAndLabelsAndProducts(newsletters = false) {
return Promise.map(DataGenerator.forKnex.labels, function (label) {
return models.Label.add(label, context.internal);
@ -705,9 +684,6 @@ const toDoList = {
members: function insertMembersAndLabelsAndProducts() {
return fixtures.insertMembersAndLabelsAndProducts(false);
},
products: function insertProducts() {
return fixtures.insertProducts();
},
newsletters: function insertNewsletters() {
return fixtures.insertNewsletters();
},

View File

@ -27,16 +27,15 @@ class Attribution {
}
/**
* Converts the instance to a parsed instance with more information about the resource included.
* Convert the instance to a parsed instance with more information about the resource included.
* It does:
* - Uses the passed model and adds a title to the attribution
* - If the resource exists and has a new url, it updates the url if possible
* - Fetch the resource and add some information about it to the attribution
* - If the resource exists and have a new url, it updates the url if possible
* - Returns an absolute URL instead of a relative one
* @param {Object|null} [model] The Post/User/Tag model of the resource associated with this attribution
* @returns {AttributionResource}
* @returns {Promise<AttributionResource>}
*/
getResource(model) {
if (!this.id || this.type === 'url' || !this.type || !model) {
async getResource() {
if (!this.id || this.type === 'url' || !this.type) {
return {
id: null,
type: 'url',
@ -45,28 +44,18 @@ class Attribution {
};
}
const updatedUrl = this.#urlTranslator.getUrlByResourceId(this.id, {absolute: true});
const resource = await this.#urlTranslator.getResourceById(this.id, this.type, {absolute: true});
return {
id: model.id,
type: this.type,
url: updatedUrl,
title: model.get('title') ?? model.get('name') ?? this.url
};
}
/**
* Same as getResource, but fetches the model by ID instead of passing it as a parameter
*/
async fetchResource() {
if (!this.id || this.type === 'url' || !this.type) {
// No fetch required
return this.getResource();
if (resource) {
return resource;
}
// Fetch model
const model = await this.#urlTranslator.getResourceById(this.id, this.type, {absolute: true});
return this.getResource(model);
return {
id: null,
type: 'url',
url: this.#urlTranslator.relativeToAbsolute(this.url),
title: this.url
};
}
}

View File

@ -24,47 +24,13 @@ class MemberAttributionService {
return this.attributionBuilder.getAttribution(history);
}
/**
* Returns the attribution resource for a given event model (MemberCreatedEvent / SubscriptionCreatedEvent), where the model has the required relations already loaded
* You need to already load the 'postAttribution', 'userAttribution', and 'tagAttribution' relations
* @param {Object} eventModel MemberCreatedEvent or SubscriptionCreatedEvent
* @returns {import('./attribution').AttributionResource|null}
*/
getEventAttribution(eventModel) {
if (eventModel.get('attribution_type') === null) {
return null;
}
const _attribution = this.attributionBuilder.build({
id: eventModel.get('attribution_id'),
url: eventModel.get('attribution_url'),
type: eventModel.get('attribution_type')
});
if (_attribution.type !== 'url') {
// Find the right relation to use to fetch the resource
const tryRelations = [
eventModel.related('postAttribution'),
eventModel.related('userAttribution'),
eventModel.related('tagAttribution')
];
for (const relation of tryRelations) {
if (relation && relation.id) {
// We need to check the ID, because .related() always returs a model when eager loaded, even when the relation didn't exist
return _attribution.getResource(relation);
}
}
}
return _attribution.getResource(null);
}
/**
* Returns the parsed attribution for a member creation event
* @param {string} memberId
* @returns {Promise<import('./attribution').AttributionResource|null>}
*/
async getMemberCreatedAttribution(memberId) {
const memberCreatedEvent = await this.models.MemberCreatedEvent.findOne({member_id: memberId}, {require: false, withRelated: []});
const memberCreatedEvent = await this.models.MemberCreatedEvent.findOne({member_id: memberId}, {require: false});
if (!memberCreatedEvent || !memberCreatedEvent.get('attribution_type')) {
return null;
}
@ -73,7 +39,7 @@ class MemberAttributionService {
url: memberCreatedEvent.get('attribution_url'),
type: memberCreatedEvent.get('attribution_type')
});
return await attribution.fetchResource();
return await attribution.getResource();
}
/**
@ -82,7 +48,7 @@ class MemberAttributionService {
* @returns {Promise<import('./attribution').AttributionResource|null>}
*/
async getSubscriptionCreatedAttribution(subscriptionId) {
const subscriptionCreatedEvent = await this.models.SubscriptionCreatedEvent.findOne({subscription_id: subscriptionId}, {require: false, withRelated: []});
const subscriptionCreatedEvent = await this.models.SubscriptionCreatedEvent.findOne({subscription_id: subscriptionId}, {require: false});
if (!subscriptionCreatedEvent || !subscriptionCreatedEvent.get('attribution_type')) {
return null;
}
@ -91,7 +57,7 @@ class MemberAttributionService {
url: subscriptionCreatedEvent.get('attribution_url'),
type: subscriptionCreatedEvent.get('attribution_type')
});
return await attribution.fetchResource();
return await attribution.getResource();
}
}

View File

@ -6,7 +6,8 @@
*/
/**
* Translate a url into, (id+type), or a resource, and vice versa
* Translate a url into a type and id
* And also in reverse
*/
class UrlTranslator {
/**
@ -80,11 +81,9 @@ class UrlTranslator {
}
}
getUrlByResourceId(id, options = {absolute: true}) {
return this.urlService.getUrlByResourceId(id, options);
}
async getResourceById(id, type, options = {absolute: true}) {
const url = this.urlService.getUrlByResourceId(id, options);
async getResourceById(id, type) {
switch (type) {
case 'post':
case 'page': {
@ -93,7 +92,12 @@ class UrlTranslator {
return null;
}
return post;
return {
id: post.id,
type,
url,
title: post.get('title')
};
}
case 'author': {
const user = await this.models.User.findOne({id}, {require: false});
@ -101,7 +105,12 @@ class UrlTranslator {
return null;
}
return user;
return {
id: user.id,
type,
url,
title: user.get('name')
};
}
case 'tag': {
const tag = await this.models.Tag.findOne({id}, {require: false});
@ -109,7 +118,12 @@ class UrlTranslator {
return null;
}
return tag;
return {
id: tag.id,
type,
url,
title: tag.get('name')
};
}
}
return null;

View File

@ -25,20 +25,17 @@ describe('AttributionBuilder', function () {
}
return;
},
getResourceById(id) {
getResourceById(id, type) {
if (id === 'invalid') {
return null;
}
return {
id,
get() {
return 'Title';
}
type,
url: 'https://absolute/dir/path',
title: 'Title'
};
},
getUrlByResourceId() {
return 'https://absolute/dir/path';
},
relativeToAbsolute(path) {
return 'https://absolute/dir' + path;
},
@ -108,7 +105,7 @@ describe('AttributionBuilder', function () {
});
it('Returns post resource', async function () {
should(await attributionBuilder.build({type: 'post', id: '123', url: '/post'}).fetchResource()).match({
should(await attributionBuilder.build({type: 'post', id: '123', url: '/post'}).getResource()).match({
type: 'post',
id: '123',
url: 'https://absolute/dir/path',
@ -117,7 +114,7 @@ describe('AttributionBuilder', function () {
});
it('Returns url resource', async function () {
should(await attributionBuilder.build({type: 'url', id: null, url: '/url'}).fetchResource()).match({
should(await attributionBuilder.build({type: 'url', id: null, url: '/url'}).getResource()).match({
type: 'url',
id: null,
url: 'https://absolute/dir/url',
@ -126,7 +123,7 @@ describe('AttributionBuilder', function () {
});
it('Returns url resource if not found', async function () {
should(await attributionBuilder.build({type: 'post', id: 'invalid', url: '/post'}).fetchResource()).match({
should(await attributionBuilder.build({type: 'post', id: 'invalid', url: '/post'}).getResource()).match({
type: 'url',
id: null,
url: 'https://absolute/dir/post',

View File

@ -9,97 +9,4 @@ describe('MemberAttributionService', function () {
new MemberAttributionService({});
});
});
describe('getEventAttribution', function () {
it('returns null if attribution_type is null', function () {
const service = new MemberAttributionService({});
const model = {
id: 'event_id',
get() {
return null;
}
};
should(service.getEventAttribution(model)).eql(null);
});
it('returns url attribution types', function () {
const service = new MemberAttributionService({
attributionBuilder: {
build(attribution) {
return {
...attribution,
getResource() {
return {
...attribution,
title: 'added'
};
}
};
}
}
});
const model = {
id: 'event_id',
get(name) {
if (name === 'attribution_type') {
return 'url';
}
if (name === 'attribution_url') {
return '/my/url/';
}
return null;
}
};
should(service.getEventAttribution(model)).eql({
id: null,
type: 'url',
url: '/my/url/',
title: 'added'
});
});
it('returns first loaded relation', function () {
const service = new MemberAttributionService({
attributionBuilder: {
build(attribution) {
return {
...attribution,
getResource() {
return {
...attribution,
title: 'added'
};
}
};
}
}
});
const model = {
id: 'event_id',
get(name) {
if (name === 'attribution_type') {
return 'user';
}
if (name === 'attribution_url') {
return '/my/url/';
}
return 'test_user_id';
},
related(name) {
if (name === 'userAttribution') {
return {
id: 'test_user_id'
};
}
return {};
}
};
should(service.getEventAttribution(model)).eql({
id: 'test_user_id',
type: 'user',
url: '/my/url/',
title: 'added'
});
});
});
});

View File

@ -111,26 +111,38 @@ describe('UrlTranslator', function () {
});
it('returns for post', async function () {
should(await translator.getResourceById('id', 'post')).match({
id: 'post_id'
should(await translator.getResourceById('id', 'post')).eql({
type: 'post',
id: 'post_id',
title: 'Title',
url: '/path'
});
});
it('returns for page', async function () {
should(await translator.getResourceById('id', 'page')).match({
id: 'post_id'
should(await translator.getResourceById('id', 'page')).eql({
type: 'page',
id: 'post_id',
title: 'Title',
url: '/path'
});
});
it('returns for tag', async function () {
should(await translator.getResourceById('id', 'tag')).match({
id: 'tag_id'
should(await translator.getResourceById('id', 'tag')).eql({
type: 'tag',
id: 'tag_id',
title: 'Title',
url: '/path'
});
});
it('returns for user', async function () {
should(await translator.getResourceById('id', 'author')).match({
id: 'user_id'
should(await translator.getResourceById('id', 'author')).eql({
type: 'author',
id: 'user_id',
title: 'Title',
url: '/path'
});
});

View File

@ -48,8 +48,6 @@ module.exports = function MembersAPI({
MemberProductEvent,
MemberEmailChangeEvent,
MemberAnalyticEvent,
MemberCreatedEvent,
SubscriptionCreatedEvent,
Offer,
OfferRedemption,
StripeProduct,
@ -107,11 +105,8 @@ module.exports = function MembersAPI({
MemberPaymentEvent,
MemberStatusEvent,
MemberLoginEvent,
MemberCreatedEvent,
SubscriptionCreatedEvent,
Comment,
labsService,
memberAttributionService
labsService
});
const memberBREADService = new MemberBREADService({

View File

@ -8,12 +8,9 @@ module.exports = class EventRepository {
MemberPaymentEvent,
MemberStatusEvent,
MemberLoginEvent,
MemberCreatedEvent,
SubscriptionCreatedEvent,
MemberPaidSubscriptionEvent,
Comment,
labsService,
memberAttributionService
labsService
}) {
this._MemberSubscribeEvent = MemberSubscribeEvent;
this._MemberPaidSubscriptionEvent = MemberPaidSubscriptionEvent;
@ -23,9 +20,6 @@ module.exports = class EventRepository {
this._EmailRecipient = EmailRecipient;
this._Comment = Comment;
this._labsService = labsService;
this._MemberCreatedEvent = MemberCreatedEvent;
this._SubscriptionCreatedEvent = SubscriptionCreatedEvent;
this._memberAttributionService = memberAttributionService;
}
async registerPayment(data) {
@ -68,38 +62,9 @@ module.exports = class EventRepository {
}
async getSubscriptionEvents(options = {}, filters = {}) {
if (!this._labsService.isSet('memberAttribution')){
options = {
...options,
withRelated: ['member'],
filter: []
};
if (filters['data.created_at']) {
options.filter.push(filters['data.created_at'].replace(/data.created_at:/g, 'created_at:'));
}
if (filters['data.member_id']) {
options.filter.push(filters['data.member_id'].replace(/data.member_id:/g, 'member_id:'));
}
options.filter = options.filter.join('+');
const {data: models, meta} = await this._MemberPaidSubscriptionEvent.findPage(options);
const data = models.map((model) => {
return {
type: 'subscription_event',
data: model.toJSON(options)
};
});
return {
data,
meta
};
}
options = {
...options,
withRelated: ['member', 'subscriptionCreatedEvent.postAttribution', 'subscriptionCreatedEvent.userAttribution', 'subscriptionCreatedEvent.tagAttribution'],
withRelated: ['member'],
filter: []
};
if (filters['data.created_at']) {
@ -115,10 +80,7 @@ module.exports = class EventRepository {
const data = models.map((model) => {
return {
type: 'subscription_event',
data: {
...model.toJSON(options),
attribution: model.get('type') === 'created' && model.related('subscriptionCreatedEvent') && model.related('subscriptionCreatedEvent').id ? this._memberAttributionService.getEventAttribution(model.related('subscriptionCreatedEvent')) : null
}
data: model.toJSON(options)
};
});
@ -187,39 +149,10 @@ module.exports = class EventRepository {
}
async getSignupEvents(options = {}, filters = {}) {
if (!this._labsService.isSet('memberAttribution')){
options = {
...options,
withRelated: ['member'],
filter: ['from_status:null']
};
if (filters['data.created_at']) {
options.filter.push(filters['data.created_at'].replace(/data.created_at:/g, 'created_at:'));
}
if (filters['data.member_id']) {
options.filter.push(filters['data.member_id'].replace(/data.member_id:/g, 'member_id:'));
}
options.filter = options.filter.join('+');
const {data: models, meta} = await this._MemberStatusEvent.findPage(options);
const data = models.map((model) => {
return {
type: 'signup_event',
data: model.toJSON(options)
};
});
return {
data,
meta
};
}
options = {
...options,
withRelated: ['member', 'postAttribution', 'userAttribution', 'tagAttribution'],
filter: []
withRelated: ['member'],
filter: ['from_status:null']
};
if (filters['data.created_at']) {
options.filter.push(filters['data.created_at'].replace(/data.created_at:/g, 'created_at:'));
@ -229,15 +162,12 @@ module.exports = class EventRepository {
}
options.filter = options.filter.join('+');
const {data: models, meta} = await this._MemberCreatedEvent.findPage(options);
const {data: models, meta} = await this._MemberStatusEvent.findPage(options);
const data = models.map((model) => {
return {
type: 'signup_event',
data: {
...model.toJSON(options),
attribution: this._memberAttributionService.getEventAttribution(model)
}
data: model.toJSON(options)
};
});