mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-27 22:49:56 -05:00
Added events to Post Analytics page (#15886)
closes TryGhost/Team#2313 - Added Sent event to Post analytics and Members feed. Now post can be Sent or Received or Bounced. - Excluded Delivered event from Sent filter on backend.
This commit is contained in:
parent
fbf761b0ac
commit
1b784b5ec5
11 changed files with 152 additions and 69 deletions
|
@ -1,9 +1,14 @@
|
||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import {action} from '@ember/object';
|
import {action} from '@ember/object';
|
||||||
|
import {inject as service} from '@ember/service';
|
||||||
|
|
||||||
export default class ActivityFeed extends Component {
|
export default class ActivityFeed extends Component {
|
||||||
|
@service feature;
|
||||||
|
|
||||||
linkScrollerTimeout = null; // needs to be global so can be cleared when needed across functions
|
linkScrollerTimeout = null; // needs to be global so can be cleared when needed across functions
|
||||||
excludedEventTypes = ['email_sent_event', 'aggregated_click_event'];
|
excludedEventTypes = this.feature.get('suppressionList')
|
||||||
|
? ['aggregated_click_event']
|
||||||
|
: ['email_sent_event', 'aggregated_click_event'];
|
||||||
|
|
||||||
@action
|
@action
|
||||||
enterLinkURL(event) {
|
enterLinkURL(event) {
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
@postId={{this.post.id}}
|
@postId={{this.post.id}}
|
||||||
@eventType="sent"
|
@eventType="sent"
|
||||||
@linkQuery={{hash filterParam=(concat "emails.post_id:" this.post.id) }}
|
@linkQuery={{hash filterParam=(concat "emails.post_id:" this.post.id) }}
|
||||||
|
@linkText="Received"
|
||||||
/>
|
/>
|
||||||
</tabs.tabPanel>
|
</tabs.tabPanel>
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@
|
||||||
@postId={{this.post.id}}
|
@postId={{this.post.id}}
|
||||||
@eventType="opened"
|
@eventType="opened"
|
||||||
@linkQuery={{hash filterParam=(concat "opened_emails.post_id:" this.post.id) }}
|
@linkQuery={{hash filterParam=(concat "opened_emails.post_id:" this.post.id) }}
|
||||||
|
@linkText="Opened"
|
||||||
/>
|
/>
|
||||||
</tabs.tabPanel>
|
</tabs.tabPanel>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -82,6 +84,7 @@
|
||||||
@postId={{this.post.id}}
|
@postId={{this.post.id}}
|
||||||
@eventType="clicked"
|
@eventType="clicked"
|
||||||
@linkQuery={{hash filterParam=(concat "clicked_links.post_id:" this.post.id) }}
|
@linkQuery={{hash filterParam=(concat "clicked_links.post_id:" this.post.id) }}
|
||||||
|
@linkText="Clicked"
|
||||||
/>
|
/>
|
||||||
</tabs.tabPanel>
|
</tabs.tabPanel>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -85,16 +85,28 @@
|
||||||
|
|
||||||
<div class="gh-post-activity-feed-footer">
|
<div class="gh-post-activity-feed-footer">
|
||||||
{{#if @linkQuery}}
|
{{#if @linkQuery}}
|
||||||
<LinkTo
|
{{#if (feature "suppressionList")}}
|
||||||
class="gh-post-activity-feed-pagination-link"
|
<div class="gh-post-activity-feed-pagination-link-wrapper">
|
||||||
@route="members"
|
{{svg-jar "filter"}}
|
||||||
@query={{@linkQuery}}
|
See members for
|
||||||
>
|
<LinkTo
|
||||||
{{svg-jar "filter"}}
|
class="gh-post-activity-feed-pagination-link"
|
||||||
See members
|
@route="members"
|
||||||
</LinkTo>
|
@query={{@linkQuery}}
|
||||||
{{else}}
|
>
|
||||||
<div></div>
|
{{@linkText}}
|
||||||
|
</LinkTo>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<LinkTo
|
||||||
|
class="gh-post-activity-feed-pagination-link"
|
||||||
|
@route="members"
|
||||||
|
@query={{@linkQuery}}
|
||||||
|
>
|
||||||
|
{{svg-jar "filter"}}
|
||||||
|
See members
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="gh-post-activity-feed-pagination">
|
<div class="gh-post-activity-feed-pagination">
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import {action} from '@ember/object';
|
import {action} from '@ember/object';
|
||||||
|
import {inject as service} from '@ember/service';
|
||||||
const eventTypes = {
|
|
||||||
sent: ['email_sent_event'],
|
|
||||||
opened: ['email_opened_event'],
|
|
||||||
clicked: ['aggregated_click_event'],
|
|
||||||
feedback: ['feedback_event'],
|
|
||||||
conversion: ['subscription_event', 'signup_event']
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class PostActivityFeed extends Component {
|
export default class PostActivityFeed extends Component {
|
||||||
|
@service feature;
|
||||||
|
|
||||||
_pageSize = 5;
|
_pageSize = 5;
|
||||||
|
_eventTypes = {
|
||||||
|
sent: this.feature.get('suppressionList')
|
||||||
|
? ['email_sent_event', 'email_delivered_event', 'email_failed_event']
|
||||||
|
: ['email_sent_event'],
|
||||||
|
opened: ['email_opened_event'],
|
||||||
|
clicked: ['aggregated_click_event'],
|
||||||
|
feedback: ['feedback_event'],
|
||||||
|
conversion: ['subscription_event', 'signup_event']
|
||||||
|
};
|
||||||
|
|
||||||
get getEventTypes() {
|
get getEventTypes() {
|
||||||
return eventTypes[this.args.eventType];
|
return this._eventTypes[this.args.eventType];
|
||||||
}
|
}
|
||||||
|
|
||||||
get pageSize() {
|
get pageSize() {
|
||||||
|
|
|
@ -10,6 +10,7 @@ export default class MembersActivityController extends Controller {
|
||||||
@service router;
|
@service router;
|
||||||
@service settings;
|
@service settings;
|
||||||
@service store;
|
@service store;
|
||||||
|
@service feature;
|
||||||
|
|
||||||
queryParams = ['excludedEvents', 'member'];
|
queryParams = ['excludedEvents', 'member'];
|
||||||
|
|
||||||
|
@ -27,8 +28,9 @@ export default class MembersActivityController extends Controller {
|
||||||
if (!this.member) {
|
if (!this.member) {
|
||||||
hiddenEvents.push(...EMAIL_EVENTS);
|
hiddenEvents.push(...EMAIL_EVENTS);
|
||||||
} else {
|
} else {
|
||||||
// Always hide sent event
|
if (!this.feature.get('suppressionList')) {
|
||||||
hiddenEvents.push('email_sent_event');
|
hiddenEvents.push('email_sent_event');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hiddenEvents.push('aggregated_click_event');
|
hiddenEvents.push('aggregated_click_event');
|
||||||
|
|
||||||
|
|
|
@ -76,8 +76,18 @@ export default class ParseMemberEventHelper extends Helper {
|
||||||
icon = 'opened-email';
|
icon = 'opened-email';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === 'email_delivered_event' || event.type === 'email_sent_event') {
|
if (this.feature.get('suppressionList')) {
|
||||||
icon = 'received-email';
|
if (event.type === 'email_sent_event') {
|
||||||
|
icon = 'sent-email';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'email_delivered_event') {
|
||||||
|
icon = 'received-email';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (event.type === 'email_delivered_event' || event.type === 'email_sent_event') {
|
||||||
|
icon = 'received-email';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === 'email_failed_event') {
|
if (event.type === 'email_failed_event') {
|
||||||
|
@ -157,8 +167,18 @@ export default class ParseMemberEventHelper extends Helper {
|
||||||
return 'opened email';
|
return 'opened email';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === 'email_delivered_event' || event.type === 'email_sent_event') {
|
if (this.feature.get('suppressionList')) {
|
||||||
return 'received email';
|
if (event.type === 'email_sent_event') {
|
||||||
|
return 'sent email';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'email_delivered_event') {
|
||||||
|
return 'received email';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (event.type === 'email_delivered_event' || event.type === 'email_sent_event') {
|
||||||
|
return 'received email';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === 'email_failed_event') {
|
if (event.type === 'email_failed_event') {
|
||||||
|
|
|
@ -1751,6 +1751,25 @@
|
||||||
filter: brightness(0.8);
|
filter: brightness(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gh-post-activity-feed-pagination-link-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.3;
|
||||||
|
color: #959595;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-post-activity-feed-pagination-link-wrapper svg {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gh-post-activity-feed-pagination-link-wrapper path {
|
||||||
|
stroke: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
.gh-post-activity-feed-pagination-link {
|
.gh-post-activity-feed-pagination-link {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
3
ghost/admin/public/assets/icons/event-sent-email.svg
Normal file
3
ghost/admin/public/assets/icons/event-sent-email.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path d="m10.835 16.242 2.414 2.404a1.22 1.22 0 0 0 1.163.332 1.24 1.24 0 0 0 .898-.82l3.965-11.885a1.24 1.24 0 0 0-1.595-1.595L5.795 8.643a1.24 1.24 0 0 0-.82.953 1.218 1.218 0 0 0 .332 1.108l3.035 3.035-.1 3.843 2.593-1.34ZM18.917 4.926 8.339 13.743" stroke="#6C747D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 422 B |
|
@ -22669,10 +22669,6 @@ Object {
|
||||||
"data": Any<Object>,
|
"data": Any<Object>,
|
||||||
"type": Any<String>,
|
"type": Any<String>,
|
||||||
},
|
},
|
||||||
Object {
|
|
||||||
"data": Any<Object>,
|
|
||||||
"type": Any<String>,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
"meta": Object {
|
"meta": Object {
|
||||||
"pagination": Object {
|
"pagination": Object {
|
||||||
|
@ -22681,17 +22677,29 @@ Object {
|
||||||
"page": null,
|
"page": null,
|
||||||
"pages": 1,
|
"pages": 1,
|
||||||
"prev": null,
|
"prev": null,
|
||||||
"total": 16,
|
"total": 15,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Activity Feed API Can filter events by post id 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": "20329",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
|
"x-powered-by": "Express",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Activity Feed API Can filter events by post id 2: [headers] 1`] = `
|
exports[`Activity Feed API Can filter events by post id 2: [headers] 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "21559",
|
"content-length": "20329",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
|
@ -22718,7 +22726,7 @@ Object {
|
||||||
"page": null,
|
"page": null,
|
||||||
"pages": 8,
|
"pages": 8,
|
||||||
"prev": null,
|
"prev": null,
|
||||||
"total": 16,
|
"total": 15,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -22915,7 +22923,7 @@ Object {
|
||||||
"page": null,
|
"page": null,
|
||||||
"pages": 2,
|
"pages": 2,
|
||||||
"prev": null,
|
"prev": null,
|
||||||
"total": 16,
|
"total": 15,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23388,9 +23396,9 @@ Object {
|
||||||
"limit": "36",
|
"limit": "36",
|
||||||
"next": null,
|
"next": null,
|
||||||
"page": null,
|
"page": null,
|
||||||
"pages": 2,
|
"pages": 1,
|
||||||
"prev": null,
|
"prev": null,
|
||||||
"total": 37,
|
"total": 36,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23492,7 +23500,7 @@ Object {
|
||||||
"page": null,
|
"page": null,
|
||||||
"pages": 2,
|
"pages": 2,
|
||||||
"prev": null,
|
"prev": null,
|
||||||
"total": 13,
|
"total": 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23521,10 +23529,6 @@ Object {
|
||||||
"data": Any<Object>,
|
"data": Any<Object>,
|
||||||
"type": Any<String>,
|
"type": Any<String>,
|
||||||
},
|
},
|
||||||
Object {
|
|
||||||
"data": Any<Object>,
|
|
||||||
"type": Any<String>,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
"meta": Object {
|
"meta": Object {
|
||||||
"pagination": Object {
|
"pagination": Object {
|
||||||
|
@ -23533,7 +23537,7 @@ Object {
|
||||||
"page": null,
|
"page": null,
|
||||||
"pages": 1,
|
"pages": 1,
|
||||||
"prev": null,
|
"prev": null,
|
||||||
"total": 3,
|
"total": 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23894,10 +23898,6 @@ Object {
|
||||||
"data": Any<Object>,
|
"data": Any<Object>,
|
||||||
"type": Any<String>,
|
"type": Any<String>,
|
||||||
},
|
},
|
||||||
Object {
|
|
||||||
"data": Any<Object>,
|
|
||||||
"type": Any<String>,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
"meta": Object {
|
"meta": Object {
|
||||||
"pagination": Object {
|
"pagination": Object {
|
||||||
|
@ -23906,17 +23906,29 @@ Object {
|
||||||
"page": null,
|
"page": null,
|
||||||
"pages": 1,
|
"pages": 1,
|
||||||
"prev": null,
|
"prev": null,
|
||||||
"total": 5,
|
"total": 4,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Activity Feed API Returns email sent events 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": "5004",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
|
"x-powered-by": "Express",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Activity Feed API Returns email sent events in activity feed 2: [headers] 1`] = `
|
exports[`Activity Feed API Returns email sent events in activity feed 2: [headers] 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "6234",
|
"content-length": "5004",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
|
|
|
@ -191,7 +191,7 @@ describe('Activity Feed API', function () {
|
||||||
// If that is ever fixed (it is difficult) we can update this test to not use a filter
|
// If that is ever fixed (it is difficult) we can update this test to not use a filter
|
||||||
// Same for click_event and aggregated_click_event (use same id)
|
// Same for click_event and aggregated_click_event (use same id)
|
||||||
const skippedTypes = ['email_opened_event', 'email_failed_event', 'email_delivered_event', 'aggregated_click_event'];
|
const skippedTypes = ['email_opened_event', 'email_failed_event', 'email_delivered_event', 'aggregated_click_event'];
|
||||||
await testPagination(skippedTypes, null, 37, 36);
|
await testPagination(skippedTypes, null, 36, 36);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can do filter based pagination for one post', async function () {
|
it('Can do filter based pagination for one post', async function () {
|
||||||
|
@ -202,7 +202,7 @@ describe('Activity Feed API', function () {
|
||||||
// Same for click_event and aggregated_click_event (use same id)
|
// Same for click_event and aggregated_click_event (use same id)
|
||||||
const skippedTypes = ['email_opened_event', 'email_failed_event', 'email_delivered_event', 'aggregated_click_event'];
|
const skippedTypes = ['email_opened_event', 'email_failed_event', 'email_delivered_event', 'aggregated_click_event'];
|
||||||
|
|
||||||
await testPagination(skippedTypes, postId, 13, 10);
|
await testPagination(skippedTypes, postId, 12, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can do filter based pagination for aggregated clicks for one post', async function () {
|
it('Can do filter based pagination for aggregated clicks for one post', async function () {
|
||||||
|
@ -335,7 +335,7 @@ describe('Activity Feed API', function () {
|
||||||
etag: anyEtag
|
etag: anyEtag
|
||||||
})
|
})
|
||||||
.matchBodySnapshot({
|
.matchBodySnapshot({
|
||||||
events: new Array(5).fill({
|
events: new Array(4).fill({
|
||||||
type: anyString,
|
type: anyString,
|
||||||
data: anyObject
|
data: anyObject
|
||||||
})
|
})
|
||||||
|
@ -396,7 +396,7 @@ describe('Activity Feed API', function () {
|
||||||
etag: anyEtag
|
etag: anyEtag
|
||||||
})
|
})
|
||||||
.matchBodySnapshot({
|
.matchBodySnapshot({
|
||||||
events: new Array(16).fill({
|
events: new Array(15).fill({
|
||||||
type: anyString,
|
type: anyString,
|
||||||
data: anyObject
|
data: anyObject
|
||||||
})
|
})
|
||||||
|
@ -416,7 +416,7 @@ describe('Activity Feed API', function () {
|
||||||
assert(body.events.find(e => e.type === 'email_opened_event'), 'Expected an email opened event');
|
assert(body.events.find(e => e.type === 'email_opened_event'), 'Expected an email opened event');
|
||||||
|
|
||||||
// Assert total is correct
|
// Assert total is correct
|
||||||
assert.equal(body.meta.pagination.total, 16);
|
assert.equal(body.meta.pagination.total, 15);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ describe('Activity Feed API', function () {
|
||||||
assert(!body.events.find(e => (e.data?.post?.id ?? e.data?.attribution?.id ?? e.data?.email?.post_id) !== postId), 'Should only return events for the post');
|
assert(!body.events.find(e => (e.data?.post?.id ?? e.data?.attribution?.id ?? e.data?.email?.post_id) !== postId), 'Should only return events for the post');
|
||||||
|
|
||||||
// Assert total is correct
|
// Assert total is correct
|
||||||
assert.equal(body.meta.pagination.total, 16);
|
assert.equal(body.meta.pagination.total, 15);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -50,7 +50,7 @@ module.exports = class EventRepository {
|
||||||
if (!options.limit) {
|
if (!options.limit) {
|
||||||
options.limit = 10;
|
options.limit = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [typeFilter, otherFilter] = this.getNQLSubset(options.filter);
|
const [typeFilter, otherFilter] = this.getNQLSubset(options.filter);
|
||||||
|
|
||||||
// Changing this order might need a change in the query functions
|
// Changing this order might need a change in the query functions
|
||||||
|
@ -173,10 +173,10 @@ module.exports = class EventRepository {
|
||||||
options = {
|
options = {
|
||||||
...options,
|
...options,
|
||||||
withRelated: [
|
withRelated: [
|
||||||
'member',
|
'member',
|
||||||
'subscriptionCreatedEvent.postAttribution',
|
'subscriptionCreatedEvent.postAttribution',
|
||||||
'subscriptionCreatedEvent.userAttribution',
|
'subscriptionCreatedEvent.userAttribution',
|
||||||
'subscriptionCreatedEvent.tagAttribution',
|
'subscriptionCreatedEvent.tagAttribution',
|
||||||
'subscriptionCreatedEvent.memberCreatedEvent',
|
'subscriptionCreatedEvent.memberCreatedEvent',
|
||||||
|
|
||||||
// This is rediculous, but we need the tier name (we'll be able to shorten this later when we switch to the subscriptions table)
|
// This is rediculous, but we need the tier name (we'll be able to shorten this later when we switch to the subscriptions table)
|
||||||
|
@ -208,7 +208,7 @@ module.exports = class EventRepository {
|
||||||
|
|
||||||
const data = models.map((model) => {
|
const data = models.map((model) => {
|
||||||
const tierName = model.related('stripeSubscription') && model.related('stripeSubscription').related('stripePrice') && model.related('stripeSubscription').related('stripePrice').related('stripeProduct') && model.related('stripeSubscription').related('stripePrice').related('stripeProduct').related('product') ? model.related('stripeSubscription').related('stripePrice').related('stripeProduct').related('product').get('name') : null;
|
const tierName = model.related('stripeSubscription') && model.related('stripeSubscription').related('stripePrice') && model.related('stripeSubscription').related('stripePrice').related('stripeProduct') && model.related('stripeSubscription').related('stripePrice').related('stripeProduct').related('product') ? model.related('stripeSubscription').related('stripePrice').related('stripeProduct').related('product').get('name') : null;
|
||||||
|
|
||||||
// Prevent toJSON on stripeSubscription (we don't have everything loaded)
|
// Prevent toJSON on stripeSubscription (we don't have everything loaded)
|
||||||
delete model.relations.stripeSubscription;
|
delete model.relations.stripeSubscription;
|
||||||
const d = {
|
const d = {
|
||||||
|
@ -298,9 +298,9 @@ module.exports = class EventRepository {
|
||||||
options = {
|
options = {
|
||||||
...options,
|
...options,
|
||||||
withRelated: [
|
withRelated: [
|
||||||
'member',
|
'member',
|
||||||
'postAttribution',
|
'postAttribution',
|
||||||
'userAttribution',
|
'userAttribution',
|
||||||
'tagAttribution'
|
'tagAttribution'
|
||||||
],
|
],
|
||||||
filter: 'subscriptionCreatedEvent.id:null+custom:true',
|
filter: 'subscriptionCreatedEvent.id:null+custom:true',
|
||||||
|
@ -415,15 +415,15 @@ module.exports = class EventRepository {
|
||||||
*/
|
*/
|
||||||
async getAggregatedClickEvents(options = {}, filter) {
|
async getAggregatedClickEvents(options = {}, filter) {
|
||||||
// This counts all clicks for a member for the same post
|
// This counts all clicks for a member for the same post
|
||||||
const postClickQuery = `SELECT count(distinct A.redirect_id)
|
const postClickQuery = `SELECT count(distinct A.redirect_id)
|
||||||
FROM members_click_events A
|
FROM members_click_events A
|
||||||
LEFT JOIN redirects A_r on A_r.id = A.redirect_id
|
LEFT JOIN redirects A_r on A_r.id = A.redirect_id
|
||||||
LEFT JOIN redirects B_r on B_r.id = members_click_events.redirect_id
|
LEFT JOIN redirects B_r on B_r.id = members_click_events.redirect_id
|
||||||
WHERE A.member_id = members_click_events.member_id AND A_r.post_id = B_r.post_id`;
|
WHERE A.member_id = members_click_events.member_id AND A_r.post_id = B_r.post_id`;
|
||||||
|
|
||||||
// Counts all clicks for the same member, for the same post, but only preceding events. This should be zero to include the event (so we only include the first events)
|
// Counts all clicks for the same member, for the same post, but only preceding events. This should be zero to include the event (so we only include the first events)
|
||||||
const postClickQueryPreceding = `SELECT count(distinct A.redirect_id)
|
const postClickQueryPreceding = `SELECT count(distinct A.redirect_id)
|
||||||
FROM members_click_events A
|
FROM members_click_events A
|
||||||
LEFT JOIN redirects A_r on A_r.id = A.redirect_id
|
LEFT JOIN redirects A_r on A_r.id = A.redirect_id
|
||||||
LEFT JOIN redirects B_r on B_r.id = members_click_events.redirect_id
|
LEFT JOIN redirects B_r on B_r.id = members_click_events.redirect_id
|
||||||
WHERE A.member_id = members_click_events.member_id AND A_r.post_id = B_r.post_id AND (A.created_at < members_click_events.created_at OR (A.created_at = members_click_events.created_at AND A.id < members_click_events.id))`;
|
WHERE A.member_id = members_click_events.member_id AND A_r.post_id = B_r.post_id AND (A.created_at < members_click_events.created_at OR (A.created_at = members_click_events.created_at AND A.id < members_click_events.id))`;
|
||||||
|
@ -499,10 +499,13 @@ module.exports = class EventRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEmailSentEvents(options = {}, filter) {
|
async getEmailSentEvents(options = {}, filter) {
|
||||||
|
const filterStr = this._labsService.isSet('suppressionList')
|
||||||
|
? 'failed_at:null+processed_at:-null+delivered_at:null+custom:true'
|
||||||
|
: 'failed_at:null+processed_at:-null+custom:true';
|
||||||
options = {
|
options = {
|
||||||
...options,
|
...options,
|
||||||
withRelated: ['member', 'email'],
|
withRelated: ['member', 'email'],
|
||||||
filter: 'failed_at:null+processed_at:-null+custom:true',
|
filter: filterStr,
|
||||||
mongoTransformer: chainTransformers(
|
mongoTransformer: chainTransformers(
|
||||||
// First set the filter manually
|
// First set the filter manually
|
||||||
replaceCustomFilterTransformer(filter),
|
replaceCustomFilterTransformer(filter),
|
||||||
|
@ -670,7 +673,7 @@ module.exports = class EventRepository {
|
||||||
* Split the filter in two parts:
|
* Split the filter in two parts:
|
||||||
* - One with 'type' that will be applied to all the pages
|
* - One with 'type' that will be applied to all the pages
|
||||||
* - Other filter that will be applied to each individual page
|
* - Other filter that will be applied to each individual page
|
||||||
*
|
*
|
||||||
* Throws if splitting is not possible (e.g. OR'ing type with other filters)
|
* Throws if splitting is not possible (e.g. OR'ing type with other filters)
|
||||||
*/
|
*/
|
||||||
getNQLSubset(filter) {
|
getNQLSubset(filter) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue