Source: client/subscribing-request.js

  1. /*
  2. * Copyright 2023, TeamDev. All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Redistribution and use in source and/or binary forms, with or without
  11. * modification, must retain the above copyright notice and the following
  12. * disclaimer.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  17. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  18. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  20. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. import {FilteringRequest} from "./filtering-request";
  27. /**
  28. * @typedef EventSubscriptionCallbacks
  29. *
  30. * A pair of callbacks that allow to add an event consumer to the subscription and to cancel it
  31. * respectively.
  32. *
  33. * @property {consumerCallback<eventConsumer>} subscribe the callback which allows to setup an
  34. * event consumer to use for the subscription
  35. * @property {parameterlessCallback} unsubscribe the callback which allows to cancel the
  36. * subscription
  37. */
  38. /**
  39. * An abstract base for requests that subscribe to messages of a certain type.
  40. *
  41. * @abstract
  42. * @template <T> the target type of messages, for events the type is always `spine.core.Event`
  43. */
  44. class SubscribingRequest extends FilteringRequest {
  45. /**
  46. * Builds a `Topic` instance based on the currently specified filters.
  47. *
  48. * @return {spine.client.Topic} a `Topic` instance
  49. */
  50. topic() {
  51. return this._builder().build();
  52. }
  53. /**
  54. * Posts a subscription request and returns the result as `Promise`.
  55. *
  56. * @return {Promise<EntitySubscriptionObject<Message> | EventSubscriptionObject>}
  57. * the asynchronously resolved subscription object
  58. */
  59. post() {
  60. const topic = this.topic();
  61. return this._subscribe(topic);
  62. }
  63. /**
  64. * @inheritDoc
  65. */
  66. _newBuilderFn() {
  67. return requestFactory => requestFactory.topic().select(this.targetType);
  68. }
  69. /**
  70. * @abstract
  71. * @return {Promise<EntitySubscriptionObject<Message> | EventSubscriptionObject>}
  72. *
  73. * @protected
  74. */
  75. _subscribe(topic) {
  76. throw new Error('Not implemented in abstract base.');
  77. }
  78. }
  79. /**
  80. * A request to subscribe to updates of entity states of a certain type.
  81. *
  82. * Allows to obtain the `EntitySubscriptionObject` which exposes the entity changes in a form of
  83. * callbacks which can be subscribed to.
  84. *
  85. * A usage example:
  86. * ```
  87. * client.subscribeTo(Task.class)
  88. * .where(Filters.eq("status", Task.Status.ACTIVE))
  89. * // Additional filtering can be done here.
  90. * .post()
  91. * .then(({itemAdded, itemChanged, itemRemoved, unsubscribe}) => {
  92. * itemAdded.subscribe(_addDisplayedTask);
  93. * itemChanged.subscribe(_changeDisplayedTask);
  94. * itemRemoved.subscribe(_removeDisplayedTask);
  95. * });
  96. * ```
  97. *
  98. * If the entity matched the subscription criteria at one point, but stopped to do so, the
  99. * `itemRemoved` callback will be triggered for it. The callback will contain the last entity state
  100. * that matched the subscription.
  101. *
  102. * Please note that the subscription object should be manually unsubscribed when it's no longer
  103. * needed to receive the updates. This can be done with the help of `unsubscribe` callback.
  104. *
  105. * @template <T> the target entity type
  106. */
  107. export class SubscriptionRequest extends SubscribingRequest {
  108. /**
  109. * @param {!Class<Message>} entityType the target entity type
  110. * @param {!Client} client the client which initiated the request
  111. * @param {!ActorRequestFactory} actorRequestFactory the request factory
  112. */
  113. constructor(entityType, client, actorRequestFactory) {
  114. super(entityType, client, actorRequestFactory)
  115. }
  116. /**
  117. * @inheritDoc
  118. *
  119. * @return {Promise<EntitySubscriptionObject<Message>>}
  120. */
  121. _subscribe(topic) {
  122. return this._client.subscribe(topic);
  123. }
  124. /**
  125. * @inheritDoc
  126. */
  127. _self() {
  128. return this;
  129. }
  130. }
  131. /**
  132. * A request to subscribe to events of a certain type.
  133. *
  134. * Allows to obtain the `EventSubscriptionObject` which reflects the events that happened in the
  135. * system and match the subscription criteria.
  136. *
  137. * A usage example:
  138. * ```
  139. * client.subscribeToEvent(TaskCreated.class)
  140. * .where([Filters.eq("task_priority", Task.Priority.HIGH),
  141. * Filters.eq("context.past_message.actor_context.actor", userId)])
  142. * .post()
  143. * .then(({eventEmitted, unsubscribe}) => {
  144. * eventEmitted.subscribe(_logEvent);
  145. * });
  146. * ```
  147. *
  148. * The fields specified to the `where` filters should either be a part of the event message or
  149. * have a `context.` prefix and address one of the fields of the `EventContext` type.
  150. *
  151. * The `eventEmitted` observable reflects all events that occurred in the system and match the
  152. * subscription criteria, in a form of `spine.core.Event`.
  153. *
  154. * Please note that the subscription object should be manually unsubscribed when it's no longer
  155. * needed to receive the updates. This can be done with the help of `unsubscribe` callback.
  156. */
  157. export class EventSubscriptionRequest extends SubscribingRequest {
  158. /**
  159. * @param {!Class<Message>} eventType the target event type
  160. * @param {!Client} client the client which initiated the request
  161. * @param {!ActorRequestFactory} actorRequestFactory the request factory
  162. */
  163. constructor(eventType, client, actorRequestFactory) {
  164. super(eventType, client, actorRequestFactory)
  165. }
  166. /**
  167. * @inheritDoc
  168. *
  169. * @return {Promise<EventSubscriptionObject>}
  170. */
  171. _subscribe(topic) {
  172. return this._client.subscribeToEvents(topic);
  173. }
  174. /**
  175. * @inheritDoc
  176. */
  177. _self() {
  178. return this;
  179. }
  180. }