0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Added model event to domain event bridge

refs https://github.com/TryGhost/Team/issues/3169

- To make the coupling to Ghost's model events as loose as possible added a bridge that maps model events to domain events. These domain events it what the collections module can subscribe to to make necessary updates.
This commit is contained in:
Naz 2023-06-21 21:00:00 +07:00 committed by naz
parent 2eb7f7dd25
commit e7a0462877
7 changed files with 78 additions and 38 deletions

View file

@ -28,6 +28,7 @@
"typescript": "5.1.3"
},
"dependencies": {
"@tryghost/domain-events": "0.0.0",
"@tryghost/errors": "^1.2.25",
"@tryghost/in-memory-repository": "0.0.0",
"@tryghost/tpl": "^0.1.25",

View file

@ -3,6 +3,7 @@ import {CollectionResourceChangeEvent} from './CollectionResourceChangeEvent';
import {CollectionRepository} from './CollectionRepository';
import tpl from '@tryghost/tpl';
import {MethodNotAllowedError, NotFoundError} from '@tryghost/errors';
import DomainEvents from '@tryghost/domain-events';
const messages = {
cannotDeleteBuiltInCollectionError: {
@ -132,6 +133,15 @@ export class CollectionsService {
return mappedDTO;
}
/**
* @description Subscribes to Domain events to update collections when posts are added, updated or deleted
*/
subscribeToEvents() {
DomainEvents.subscribe(CollectionResourceChangeEvent, async (event: CollectionResourceChangeEvent) => {
await this.updateCollections(event);
});
}
async createCollection(data: CollectionInputDTO): Promise<CollectionDTO> {
const collection = await Collection.create({
title: data.title,

View file

@ -1,2 +1,3 @@
declare module '@tryghost/errors';
declare module '@tryghost/tpl';
declare module '@tryghost/domain-events'

View file

@ -1,4 +1,6 @@
import assert from 'assert';
import assert from 'assert/strict';
import sinon from 'sinon';
import DomainEvents from '@tryghost/domain-events';
import {
CollectionsService,
CollectionsRepositoryInMemory,
@ -259,6 +261,24 @@ describe('CollectionsService', function () {
});
});
describe('subscribeToEvents', function () {
it('Subscribes to Domain Events', function () {
const updateCollectionsSpy = sinon.spy(collectionsService, 'updateCollections');
const collectionChangeEvent = CollectionResourceChangeEvent.create('post.added', {
id: 'test-id',
resource: 'post'
});
DomainEvents.dispatch(collectionChangeEvent);
assert.equal(updateCollectionsSpy.calledOnce, false, 'updateCollections should not be called yet');
collectionsService.subscribeToEvents();
DomainEvents.dispatch(collectionChangeEvent);
assert.equal(updateCollectionsSpy.calledOnce, true, 'updateCollections should be called');
});
});
describe('Automatic Collections', function () {
it('Can create an automatic collection', async function () {
const collection = await collectionsService.createCollection({

View file

@ -1,7 +1,6 @@
const {
CollectionsService,
CollectionsRepositoryInMemory,
CollectionResourceChangeEvent
CollectionsRepositoryInMemory
} = require('@tryghost/collections');
const labs = require('../../../shared/labs');
@ -26,8 +25,7 @@ class CollectionsServiceWrapper {
return;
}
const events = require('../../lib/common/events');
const translateModelEventsToDomainEvents = require('./model-to-domain-events-bridge');
const existingBuiltins = await this.api.getAll({filter: 'slug:featured'});
if (!existingBuiltins.data.length) {
@ -50,24 +48,8 @@ class CollectionsServiceWrapper {
});
}
const ghostModelUpdateEvents = require('./update-events');
const collectionListener = (event, data) => {
const change = Object.assign({}, {
id: data.id,
resource: event.split('.')[0]
}, data._changed);
const collectionResourceChangeEvent = CollectionResourceChangeEvent.create(event, change);
// @NOTE: to avoid race conditions we need a queue here to make sure updates happen
// one by one and not in parallel
this.api.updateCollections(collectionResourceChangeEvent);
};
for (const event of ghostModelUpdateEvents) {
if (!events.hasRegisteredListener(event, 'collectionListener')) {
events.on(event, data => collectionListener(event, data));
}
}
this.api.subscribeToEvents();
translateModelEventsToDomainEvents();
}
}

View file

@ -0,0 +1,41 @@
const DomainEvents = require('@tryghost/domain-events');
const {
CollectionResourceChangeEvent
} = require('@tryghost/collections');
const domainEventDispatcher = (modelEventName, data) => {
const change = Object.assign({}, {
id: data.id,
resource: modelEventName.split('.')[0]
}, data._changed);
const collectionResourceChangeEvent = CollectionResourceChangeEvent.create(modelEventName, change);
DomainEvents.dispatch(collectionResourceChangeEvent);
};
const translateModelEventsToDomainEvents = () => {
const events = require('../../lib/common/events');
const ghostModelUpdateEvents = [
'post.published',
'post.published.edited',
'post.unpublished',
'tag.added',
'tag.edited',
'tag.attached',
'tag.detached',
'tag.deleted',
'user.activated',
'user.activated.edited',
'user.attached',
'user.detached',
'user.deleted'
];
for (const modelEvent of ghostModelUpdateEvents) {
if (!events.hasRegisteredListener(modelEvent, 'collectionListener')) {
events.on(modelEvent, data => domainEventDispatcher(modelEvent, data));
}
}
};
module.exports = translateModelEventsToDomainEvents;

View file

@ -1,15 +0,0 @@
module.exports = [
'post.published',
'post.published.edited',
'post.unpublished',
'tag.added',
'tag.edited',
'tag.attached',
'tag.detached',
'tag.deleted',
'user.activated',
'user.activated.edited',
'user.attached',
'user.detached',
'user.deleted'
];