repository-observer.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import {Service} from '../service/index.js';
  2. import {isPromise} from 'mocha/lib/utils.js';
  3. import {InvalidArgumentError} from '../errors/index.js';
  4. import {DefinitionRegistry} from '../definition/index.js';
  5. /**
  6. * Repository event.
  7. *
  8. * @type {{
  9. * BEFORE_UPDATE: string,
  10. * AFTER_READ: string,
  11. * BEFORE_READ: string,
  12. * AFTER_DELETE: string,
  13. * AFTER_CREATE: string,
  14. * BEFORE_DELETE: string,
  15. * BEFORE_CREATE: string,
  16. * AFTER_UPDATE: string,
  17. * }}
  18. */
  19. export const RepositoryEvent = {
  20. BEFORE_CREATE: 'beforeCreate',
  21. BEFORE_READ: 'beforeRead',
  22. BEFORE_UPDATE: 'beforeUpdate',
  23. BEFORE_DELETE: 'beforeDelete',
  24. AFTER_CREATE: 'afterCreate',
  25. AFTER_READ: 'afterRead',
  26. AFTER_UPDATE: 'afterUpdate',
  27. AFTER_DELETE: 'afterDelete',
  28. };
  29. /**
  30. * Repository observer.
  31. */
  32. export class RepositoryObserver extends Service {
  33. /**
  34. * Handlers map.
  35. *
  36. * @example
  37. * ```
  38. * const eventsMap = this._handlersMap.get(modelName) || new Map();
  39. * const handlers = eventsMap.get(eventName) || [];
  40. * ```
  41. *
  42. * @type {Map<any, any>}
  43. * @private
  44. */
  45. _handlersMap = new Map();
  46. /**
  47. * Observe.
  48. *
  49. * @param modelName
  50. * @param eventName
  51. * @param handler
  52. */
  53. observe(modelName, eventName, handler) {
  54. if (arguments.length === 2) {
  55. handler = eventName;
  56. eventName = modelName;
  57. modelName = null;
  58. }
  59. if (modelName || arguments.length === 3) {
  60. if (!modelName || typeof modelName !== 'string')
  61. throw new InvalidArgumentError(
  62. 'The parameter "modelName" of RepositoryObserver.observe ' +
  63. 'must be a non-empty String, but %s given.',
  64. modelName,
  65. );
  66. if (!this.get(DefinitionRegistry).hasModel(modelName))
  67. throw new InvalidArgumentError(
  68. 'Cannot observe repository of a not defined model %s.',
  69. modelName,
  70. );
  71. }
  72. if (!eventName || typeof eventName !== 'string') {
  73. throw new InvalidArgumentError(
  74. 'The parameter "eventName" of RepositoryObserver.observe ' +
  75. 'must be a non-empty String, but %s given.',
  76. eventName,
  77. );
  78. }
  79. if (!handler || typeof handler !== 'function')
  80. throw new InvalidArgumentError(
  81. 'The parameter "handler" of RepositoryObserver.observe ' +
  82. 'must be a Function, but %s given.',
  83. handler,
  84. );
  85. const eventsMap = this._handlersMap.get(modelName) ?? new Map();
  86. const handlers = eventsMap.get(eventName) ?? [];
  87. handlers.push(handler);
  88. eventsMap.set(eventName, handlers);
  89. this._handlersMap.set(modelName, eventsMap);
  90. }
  91. /**
  92. * Get handlers.
  93. *
  94. * @param {string} modelName
  95. * @param {string} eventName
  96. * @return {function[]}
  97. * @private
  98. */
  99. _getHandlers(modelName, eventName) {
  100. if (!modelName || typeof modelName !== 'string')
  101. throw new InvalidArgumentError(
  102. 'The parameter "modelName" of RepositoryObserver._getHandlers ' +
  103. 'must be a non-empty String, but %s given.',
  104. modelName,
  105. );
  106. if (!eventName || typeof eventName !== 'string')
  107. throw new InvalidArgumentError(
  108. 'The parameter "eventName" of RepositoryObserver._getHandlers ' +
  109. 'must be a non-empty String, but %s given.',
  110. eventName,
  111. );
  112. const rootEventsMap = this._handlersMap.get(null) ?? new Map();
  113. const rootHandlers = rootEventsMap.get(eventName) ?? [];
  114. const modelEventsMap = this._handlersMap.get(modelName) ?? new Map();
  115. const modelHandlers = modelEventsMap.get(eventName) ?? [];
  116. return [...rootHandlers, ...modelHandlers];
  117. }
  118. /**
  119. * Emit.
  120. *
  121. * @param {string} modelName
  122. * @param {string} eventName
  123. * @param {string} methodName
  124. * @param {object} context
  125. * @return {Promise<unknown[]>}
  126. */
  127. emit(modelName, eventName, methodName, context) {
  128. if (!modelName || typeof modelName !== 'string')
  129. throw new InvalidArgumentError(
  130. 'The parameter "modelName" of RepositoryObserver.emit ' +
  131. 'must be a non-empty String, but %s given.',
  132. modelName,
  133. );
  134. if (!eventName || typeof eventName !== 'string')
  135. throw new InvalidArgumentError(
  136. 'The parameter "eventName" of RepositoryObserver.emit ' +
  137. 'must be a non-empty String, but %s given.',
  138. eventName,
  139. );
  140. if (!methodName || typeof methodName !== 'string')
  141. throw new InvalidArgumentError(
  142. 'The parameter "methodName" of RepositoryObserver.emit ' +
  143. 'must be a non-empty String, but %s given.',
  144. methodName,
  145. );
  146. if (!context || typeof context !== 'object' || Array.isArray(context))
  147. throw new InvalidArgumentError(
  148. 'The parameter "context" of RepositoryObserver.emit ' +
  149. 'must be an Object, but %s given.',
  150. context,
  151. );
  152. const promises = [];
  153. const handlers = this._getHandlers(modelName, eventName);
  154. handlers.forEach(handler => {
  155. const result = handler({modelName, eventName, methodName, ...context});
  156. if (isPromise(result)) promises.push(result);
  157. });
  158. return Promise.all(promises);
  159. }
  160. }