Ghost/ghost/member-attribution/lib/service.js
Simon Backx f124d142c9 Added member attributions to activity feed (#15283)
refs https://github.com/TryGhost/Team/issues/1833
refs https://github.com/TryGhost/Team/issues/1834

We've added the attribution property to subscription and signup events when the
flag is enabled. The attributions resource is fetched by creating multiple relations
on the model, rather than polymorphic as we ran into issues with that as they can't
be nullable/optional.

The parse-member-event structure has been updated to make it easier to work with,
specifically `getObject` is only used when the event is clickable, and there is now a 
join property which makes it easier to join the action and the object.
2022-08-24 11:17:28 -04:00

99 lines
3.9 KiB
JavaScript

const UrlHistory = require('./history');
class MemberAttributionService {
/**
*
* @param {Object} deps
* @param {Object} deps.attributionBuilder
* @param {Object} deps.models
* @param {Object} deps.models.MemberCreatedEvent
* @param {Object} deps.models.SubscriptionCreatedEvent
*/
constructor({attributionBuilder, models}) {
this.models = models;
this.attributionBuilder = attributionBuilder;
}
/**
*
* @param {import('./history').UrlHistoryArray} historyArray
* @returns {import('./attribution').Attribution}
*/
getAttribution(historyArray) {
const history = new UrlHistory(historyArray);
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: []});
if (!memberCreatedEvent || !memberCreatedEvent.get('attribution_type')) {
return null;
}
const attribution = this.attributionBuilder.build({
id: memberCreatedEvent.get('attribution_id'),
url: memberCreatedEvent.get('attribution_url'),
type: memberCreatedEvent.get('attribution_type')
});
return await attribution.fetchResource();
}
/**
* Returns the last attribution for a given subscription ID
* @param {string} subscriptionId
* @returns {Promise<import('./attribution').AttributionResource|null>}
*/
async getSubscriptionCreatedAttribution(subscriptionId) {
const subscriptionCreatedEvent = await this.models.SubscriptionCreatedEvent.findOne({subscription_id: subscriptionId}, {require: false, withRelated: []});
if (!subscriptionCreatedEvent || !subscriptionCreatedEvent.get('attribution_type')) {
return null;
}
const attribution = this.attributionBuilder.build({
id: subscriptionCreatedEvent.get('attribution_id'),
url: subscriptionCreatedEvent.get('attribution_url'),
type: subscriptionCreatedEvent.get('attribution_type')
});
return await attribution.fetchResource();
}
}
module.exports = MemberAttributionService;