Skip to content

Commit

Permalink
Build in support for subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
coladarci committed Sep 28, 2018
1 parent 5560af0 commit 600c9ab
Showing 1 changed file with 105 additions and 9 deletions.
114 changes: 105 additions & 9 deletions addon/services/apollo.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,55 @@ import QueryManager from 'ember-apollo-client/apollo/query-manager';
import copyWithExtras from 'ember-apollo-client/utils/copy-with-extras';
import { registerWaiter } from '@ember/test';
import fetch from 'fetch';
import Evented from '@ember/object/evented';

const Subscription = EmberObject.extend(Evented, {
init(...args) {
this._super(...args);

this.set('events', []);
},

lastEvent: alias('events.firstObject'),

apolloUnsubscribe() {
this.get('_apolloClientSubscription').unsubscribe();
},

_apolloClientSubscription: null,

_onNewData(newData) {
this.get('events').unshiftObject(newData);
this.trigger('event', newData);
},
});

function extractNewData(resultKey, { data, loading }) {
if (loading && data === undefined) {
// This happens when the cache has no data and the data is still loading
// from the server. We don't want to resolve the promise with empty data
// so we instead just bail out.
//
// See https://github.com/bgentry/ember-apollo-client/issues/45
return null;
}
let keyedData = isNone(resultKey) ? data : data && get(data, resultKey);

return copyWithExtras(keyedData || {}, [], []);
}

function newDataFunc(observable, resultKey, resolve, mergedProps = {}) {
let obj;
mergedProps[apolloObservableKey] = observable;

return ({ data, loading }) => {
if (loading && data === undefined) {
// This happens when the cache has no data and the data is still loading
// from the server. We don't want to resolve the promise with empty data
// so we instead just bail out.
//
// See https://github.com/bgentry/ember-apollo-client/issues/45
return newData => {
let dataToSend = extractNewData(resultKey, newData);

if (dataToSend === null) {
// see comment in extractNewData
return;
}
let keyedData = isNone(resultKey) ? data : data && get(data, resultKey);
let dataToSend = copyWithExtras(keyedData || {}, [], []);

if (isNone(obj)) {
if (isArray(dataToSend)) {
obj = A(dataToSend);
Expand Down Expand Up @@ -197,6 +230,50 @@ export default Service.extend({
);
},

/**
* Executes a `subscribe` on the Apollo client. If this subscription receives
* data, the resolved object will be updated with the new data.
*
* When using this method, it is important to call `apolloUnsubscribe()` on
* the resolved data when the route or component is torn down. That tells
* Apollo to stop trying to send updated data to a non-existent listener.
*
* @method subscribe
* @param {!Object} opts The query options used in the Apollo Client subscribe.
* @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
* @return {!Promise}
* @public
*/
subscribe(opts, resultKey = null) {
const observable = this.get('client').subscribe(opts);

const obj = Subscription.create();

return this._waitFor(
new RSVP.Promise((resolve, reject) => {
let subscription = observable.subscribe({
next: newData => {
let dataToSend = extractNewData(resultKey, newData);

if (dataToSend === null) {
// see comment in extractNewData
return;
}

run(() => obj._onNewData(dataToSend));
},
error(e) {
reject(e);
},
});

obj.set('_apolloClientSubscription', subscription);

resolve(obj);
})
);
},

/**
* Executes a single `query` on the Apollo client. The resolved object will
* never be updated and does not have to be unsubscribed.
Expand Down Expand Up @@ -253,6 +330,25 @@ export default Service.extend({
);
},

/**
* Executes a `subscribe` on the Apollo client and tracks the resulting
* subscription on the provided query manager.
*
* @method managedSubscribe
* @param {!Object} manager A QueryManager that should track this active subscribe.
* @param {!Object} opts The query options used in the Apollo Client subscribe.
* @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
* @return {!Promise}
* @private
*/
managedSubscribe(manager, opts, resultKey = null) {
return this.subscribe(opts, resultKey).then(obj => {
manager.trackSubscription(obj._apolloClientSubscription);

return obj;
});
},

createQueryManager() {
return QueryManager.create({ apollo: this });
},
Expand Down

0 comments on commit 600c9ab

Please sign in to comment.