summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Hanzel <mhanzel@gitlab.com>2019-08-15 16:43:29 -0400
committerMartin Hanzel <mhanzel@gitlab.com>2019-08-15 16:58:55 -0400
commit23906d6478b46fe0ac21f22d21321f9a9f0b9f81 (patch)
treebc64e0b8a6001fb017fa0c287c2568354f5866e3
parent23754943a7ec119f123694a93c79fc07c32b7ba5 (diff)
downloadgitlab-ce-mh/pubsub.tar.gz
Add pubsub messaging systemmh/pubsub
-rw-r--r--app/assets/javascripts/lib/utils/pubsub.js36
-rw-r--r--spec/frontend/lib/utils/pubsub_spec.js41
2 files changed, 77 insertions, 0 deletions
diff --git a/app/assets/javascripts/lib/utils/pubsub.js b/app/assets/javascripts/lib/utils/pubsub.js
new file mode 100644
index 00000000000..6a9da4e8003
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/pubsub.js
@@ -0,0 +1,36 @@
+/**
+ * @module message_queue
+ *
+ * Implements a simple, async messaging system with pub/sub mechanics. Useful for getting jQuery controls and Vue components to talk to each other.
+ */
+
+const topics = {};
+
+/**
+ * Publish a message to a topic with an optional payload. Listeners are invoked asynchronously. If there are no listeners for a topic, nothing happens.
+ */
+export function publish(topic, payload) {
+ const handlers = topics[topic];
+ if (!handlers) return;
+
+ handlers.forEach(handler => {
+ Promise.resolve()
+ .then(() => handler(payload))
+ .catch(e => {
+ throw e;
+ });
+ });
+}
+
+/**
+ * Subscribes to a topic. When a message is published on that topic, the handler will by called.
+ * @returns {Function} A function that unsubscribes the handler from the topic.
+ */
+export function subscribe(topic, handler) {
+ topics[topic] = topics[topic] || [];
+ topics[topic].push(handler);
+
+ return () => {
+ topics[topic] = topics[topic].filter(h => h !== handler);
+ };
+}
diff --git a/spec/frontend/lib/utils/pubsub_spec.js b/spec/frontend/lib/utils/pubsub_spec.js
new file mode 100644
index 00000000000..244e6d3bbc8
--- /dev/null
+++ b/spec/frontend/lib/utils/pubsub_spec.js
@@ -0,0 +1,41 @@
+import { publish, subscribe } from '~/lib/utils/pubsub';
+
+describe('Pub/sub messaging', () => {
+ it('sends and receives messages asynchronously', done => {
+ const receiver1 = jest.fn();
+ const receiver2 = jest.fn();
+ const receiver3 = jest.fn();
+ subscribe('namespace:topic', receiver1);
+ subscribe('namespace:topic', receiver2);
+ subscribe('namespace:topic2', receiver3);
+
+ publish('shoudnotreceive', 'shouldnotreceive');
+ publish('namespace:topic', 1);
+ publish('namespace:topic', 2);
+ publish('namespace:topic2', 3);
+
+ // Receivers should not be called synchronously
+ expect(receiver1).not.toHaveBeenCalled();
+ expect(receiver2).not.toHaveBeenCalled();
+ expect(receiver3).not.toHaveBeenCalled();
+
+ setImmediate(() => {
+ expect(receiver1.mock.calls).toEqual([[1], [2]]);
+ expect(receiver2.mock.calls).toEqual([[1], [2]]);
+ expect(receiver3.mock.calls).toEqual([[3]]);
+ done();
+ });
+ });
+
+ it('allows clients to unsubscribe', done => {
+ const receiver = jest.fn();
+ const unsubscribe = subscribe('topic', receiver);
+ publish('topic', 1);
+ unsubscribe();
+ publish('topic', 2);
+ setImmediate(() => {
+ expect(receiver.mock.calls).toEqual([[1]]);
+ done();
+ });
+ });
+});