apollo-client
Version:
A simple yet functional GraphQL client.
646 lines • 28.9 kB
JavaScript
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
import { Deduplicator, } from '../transport/Deduplicator';
import { isEqual } from '../util/isEqual';
import { FetchType, } from './types';
import { NetworkStatus, isNetworkRequestInFlight, } from '../queries/networkStatus';
import { getDataWithOptimisticResults, } from '../store';
import { checkDocument, getQueryDefinition, getOperationName, } from '../queries/getFromAST';
import { addTypenameToDocument, } from '../queries/queryTransform';
import { createStoreReducer, } from '../data/resultReducers';
import { isProduction, } from '../util/environment';
import maybeDeepFreeze from '../util/maybeDeepFreeze';
import { print } from 'graphql-tag/printer';
import { readQueryFromStore, } from '../data/readFromStore';
import { diffQueryAgainstStore, } from '../data/readFromStore';
import { QueryScheduler, } from '../scheduler/scheduler';
import { Observable, } from '../util/Observable';
import { isApolloError, ApolloError, } from '../errors/ApolloError';
import { ObservableQuery } from './ObservableQuery';
var QueryManager = (function () {
function QueryManager(_a) {
var networkInterface = _a.networkInterface, store = _a.store, reduxRootSelector = _a.reduxRootSelector, _b = _a.reducerConfig, reducerConfig = _b === void 0 ? { mutationBehaviorReducers: {} } : _b, resultTransformer = _a.resultTransformer, resultComparator = _a.resultComparator, _c = _a.addTypename, addTypename = _c === void 0 ? true : _c, _d = _a.queryDeduplication, queryDeduplication = _d === void 0 ? false : _d;
var _this = this;
this.idCounter = 1;
this.networkInterface = networkInterface;
this.deduplicator = new Deduplicator(networkInterface);
this.store = store;
this.reduxRootSelector = reduxRootSelector;
this.reducerConfig = reducerConfig;
this.resultTransformer = resultTransformer;
this.resultComparator = resultComparator;
this.pollingTimers = {};
this.queryListeners = {};
this.queryDocuments = {};
this.addTypename = addTypename;
this.queryDeduplication = queryDeduplication;
this.scheduler = new QueryScheduler({
queryManager: this,
});
this.fetchQueryPromises = {};
this.observableQueries = {};
this.queryIdsByName = {};
if (this.store['subscribe']) {
var currentStoreData_1;
this.store['subscribe'](function () {
var previousStoreData = currentStoreData_1 || {};
var previousStoreHasData = Object.keys(previousStoreData).length;
currentStoreData_1 = _this.getApolloState();
if (isEqual(previousStoreData, currentStoreData_1) && previousStoreHasData) {
return;
}
_this.broadcastQueries();
});
}
}
QueryManager.prototype.broadcastNewStore = function (store) {
this.broadcastQueries();
};
QueryManager.prototype.mutate = function (_a) {
var _this = this;
var mutation = _a.mutation, variables = _a.variables, optimisticResponse = _a.optimisticResponse, updateQueriesByName = _a.updateQueries, _b = _a.refetchQueries, refetchQueries = _b === void 0 ? [] : _b;
var mutationId = this.generateQueryId();
if (this.addTypename) {
mutation = addTypenameToDocument(mutation);
}
checkDocument(mutation);
var mutationString = print(mutation);
var request = {
query: mutation,
variables: variables,
operationName: getOperationName(mutation),
};
this.queryDocuments[mutationId] = mutation;
var updateQueries = {};
if (updateQueriesByName) {
Object.keys(updateQueriesByName).forEach(function (queryName) { return (_this.queryIdsByName[queryName] || []).forEach(function (queryId) {
updateQueries[queryId] = updateQueriesByName[queryName];
}); });
}
this.store.dispatch({
type: 'APOLLO_MUTATION_INIT',
mutationString: mutationString,
mutation: mutation,
variables: variables || {},
operationName: getOperationName(mutation),
mutationId: mutationId,
optimisticResponse: optimisticResponse,
extraReducers: this.getExtraReducers(),
updateQueries: updateQueries,
});
return new Promise(function (resolve, reject) {
_this.networkInterface.query(request)
.then(function (result) {
if (result.errors) {
reject(new ApolloError({
graphQLErrors: result.errors,
}));
}
_this.store.dispatch({
type: 'APOLLO_MUTATION_RESULT',
result: result,
mutationId: mutationId,
document: mutation,
operationName: getOperationName(mutation),
variables: variables || {},
extraReducers: _this.getExtraReducers(),
updateQueries: updateQueries,
});
var reducerError = _this.getApolloState().reducerError;
if (reducerError) {
reject(reducerError);
return;
}
if (typeof refetchQueries[0] === 'string') {
refetchQueries.forEach(function (name) { _this.refetchQueryByName(name); });
}
else {
refetchQueries.forEach(function (pureQuery) {
_this.query({
query: pureQuery.query,
variables: pureQuery.variables,
forceFetch: true,
});
});
}
delete _this.queryDocuments[mutationId];
resolve(_this.transformResult(result));
})
.catch(function (err) {
_this.store.dispatch({
type: 'APOLLO_MUTATION_ERROR',
error: err,
mutationId: mutationId,
});
delete _this.queryDocuments[mutationId];
reject(new ApolloError({
networkError: err,
}));
});
});
};
QueryManager.prototype.queryListenerForObserver = function (queryId, options, observer) {
var _this = this;
var lastResult;
return function (queryStoreValue) {
if (!queryStoreValue) {
return;
}
var noFetch = _this.observableQueries[queryId] ? _this.observableQueries[queryId].observableQuery.options.noFetch : options.noFetch;
var shouldNotifyIfLoading = queryStoreValue.returnPartialData
|| queryStoreValue.previousVariables || noFetch;
var networkStatusChanged = lastResult && queryStoreValue.networkStatus !== lastResult.networkStatus;
if (!isNetworkRequestInFlight(queryStoreValue.networkStatus) ||
(networkStatusChanged && options.notifyOnNetworkStatusChange) ||
shouldNotifyIfLoading) {
if ((queryStoreValue.graphQLErrors && queryStoreValue.graphQLErrors.length > 0) ||
queryStoreValue.networkError) {
var apolloError = new ApolloError({
graphQLErrors: queryStoreValue.graphQLErrors,
networkError: queryStoreValue.networkError,
});
if (observer.error) {
try {
observer.error(apolloError);
}
catch (e) {
console.error("Error in observer.error \n" + e.stack);
}
}
else {
console.error('Unhandled error', apolloError, apolloError.stack);
if (!isProduction()) {
console.info('An unhandled error was thrown because no error handler is registered ' +
'for the query ' + queryStoreValue.queryString);
}
}
}
else {
try {
var resultFromStore = {
data: readQueryFromStore({
store: _this.getDataWithOptimisticResults(),
query: _this.queryDocuments[queryId],
variables: queryStoreValue.previousVariables || queryStoreValue.variables,
returnPartialData: options.returnPartialData || noFetch,
config: _this.reducerConfig,
previousResult: lastResult && lastResult.data,
}),
loading: isNetworkRequestInFlight(queryStoreValue.networkStatus),
networkStatus: queryStoreValue.networkStatus,
};
if (observer.next) {
var isDifferentResult = _this.resultComparator ? !_this.resultComparator(lastResult, resultFromStore) : !(lastResult &&
resultFromStore &&
lastResult.networkStatus === resultFromStore.networkStatus &&
lastResult.data === resultFromStore.data);
if (isDifferentResult) {
lastResult = resultFromStore;
try {
observer.next(maybeDeepFreeze(_this.transformResult(resultFromStore)));
}
catch (e) {
console.error("Error in observer.next \n" + e.stack);
}
}
}
}
catch (error) {
if (observer.error) {
observer.error(new ApolloError({
networkError: error,
}));
}
return;
}
}
}
};
};
QueryManager.prototype.watchQuery = function (options, shouldSubscribe) {
if (shouldSubscribe === void 0) { shouldSubscribe = true; }
getQueryDefinition(options.query);
var transformedOptions = __assign({}, options);
if (this.addTypename) {
transformedOptions.query = addTypenameToDocument(transformedOptions.query);
}
var observableQuery = new ObservableQuery({
scheduler: this.scheduler,
options: transformedOptions,
shouldSubscribe: shouldSubscribe,
});
return observableQuery;
};
QueryManager.prototype.query = function (options) {
var _this = this;
if (options.returnPartialData) {
throw new Error('returnPartialData option only supported on watchQuery.');
}
if (options.query.kind !== 'Document') {
throw new Error('You must wrap the query string in a "gql" tag.');
}
var requestId = this.idCounter;
var resPromise = new Promise(function (resolve, reject) {
_this.addFetchQueryPromise(requestId, resPromise, resolve, reject);
return _this.watchQuery(options, false).result().then(function (result) {
_this.removeFetchQueryPromise(requestId);
resolve(result);
}).catch(function (error) {
_this.removeFetchQueryPromise(requestId);
reject(error);
});
});
return resPromise;
};
QueryManager.prototype.fetchQuery = function (queryId, options, fetchType) {
var _a = options.variables, variables = _a === void 0 ? {} : _a, _b = options.forceFetch, forceFetch = _b === void 0 ? false : _b, _c = options.returnPartialData, returnPartialData = _c === void 0 ? false : _c, _d = options.noFetch, noFetch = _d === void 0 ? false : _d, _e = options.metadata, metadata = _e === void 0 ? null : _e;
var queryDoc = this.transformQueryDocument(options).queryDoc;
var queryString = print(queryDoc);
var storeResult;
var needToFetch = forceFetch;
if (!forceFetch) {
var _f = diffQueryAgainstStore({
query: queryDoc,
store: this.reduxRootSelector(this.store.getState()).data,
returnPartialData: true,
variables: variables,
config: this.reducerConfig,
}), isMissing = _f.isMissing, result = _f.result;
needToFetch = isMissing || false;
storeResult = result;
}
var requestId = this.generateRequestId();
var shouldFetch = needToFetch && !noFetch;
this.queryDocuments[queryId] = queryDoc;
this.store.dispatch({
type: 'APOLLO_QUERY_INIT',
queryString: queryString,
document: queryDoc,
variables: variables,
forceFetch: forceFetch,
returnPartialData: returnPartialData || noFetch,
queryId: queryId,
requestId: requestId,
storePreviousVariables: shouldFetch,
isPoll: fetchType === FetchType.poll,
isRefetch: fetchType === FetchType.refetch,
metadata: metadata,
});
if (!shouldFetch || returnPartialData) {
this.store.dispatch({
type: 'APOLLO_QUERY_RESULT_CLIENT',
result: { data: storeResult },
variables: variables,
document: queryDoc,
complete: !shouldFetch,
queryId: queryId,
requestId: requestId,
});
}
if (shouldFetch) {
return this.fetchRequest({
requestId: requestId,
queryId: queryId,
document: queryDoc,
options: options,
});
}
return Promise.resolve({ data: storeResult });
};
QueryManager.prototype.generateQueryId = function () {
var queryId = this.idCounter.toString();
this.idCounter++;
return queryId;
};
QueryManager.prototype.stopQueryInStore = function (queryId) {
this.store.dispatch({
type: 'APOLLO_QUERY_STOP',
queryId: queryId,
});
};
;
QueryManager.prototype.getApolloState = function () {
return this.reduxRootSelector(this.store.getState());
};
QueryManager.prototype.selectApolloState = function (store) {
return this.reduxRootSelector(store.getState());
};
QueryManager.prototype.getInitialState = function () {
return { data: this.getApolloState().data };
};
QueryManager.prototype.getDataWithOptimisticResults = function () {
return getDataWithOptimisticResults(this.getApolloState());
};
QueryManager.prototype.addQueryListener = function (queryId, listener) {
this.queryListeners[queryId] = this.queryListeners[queryId] || [];
this.queryListeners[queryId].push(listener);
};
QueryManager.prototype.addFetchQueryPromise = function (requestId, promise, resolve, reject) {
this.fetchQueryPromises[requestId.toString()] = { promise: promise, resolve: resolve, reject: reject };
};
QueryManager.prototype.removeFetchQueryPromise = function (requestId) {
delete this.fetchQueryPromises[requestId.toString()];
};
QueryManager.prototype.addObservableQuery = function (queryId, observableQuery) {
this.observableQueries[queryId] = { observableQuery: observableQuery };
var queryDef = getQueryDefinition(observableQuery.options.query);
if (queryDef.name && queryDef.name.value) {
var queryName = queryDef.name.value;
this.queryIdsByName[queryName] = this.queryIdsByName[queryName] || [];
this.queryIdsByName[queryName].push(observableQuery.queryId);
}
};
QueryManager.prototype.removeObservableQuery = function (queryId) {
var observableQuery = this.observableQueries[queryId].observableQuery;
var definition = getQueryDefinition(observableQuery.options.query);
var queryName = definition.name ? definition.name.value : null;
delete this.observableQueries[queryId];
if (queryName) {
this.queryIdsByName[queryName] = this.queryIdsByName[queryName].filter(function (val) {
return !(observableQuery.queryId === val);
});
}
};
QueryManager.prototype.resetStore = function () {
var _this = this;
Object.keys(this.fetchQueryPromises).forEach(function (key) {
var reject = _this.fetchQueryPromises[key].reject;
reject(new Error('Store reset while query was in flight.'));
});
this.store.dispatch({
type: 'APOLLO_STORE_RESET',
observableQueryIds: Object.keys(this.observableQueries),
});
Object.keys(this.observableQueries).forEach(function (queryId) {
var storeQuery = _this.reduxRootSelector(_this.store.getState()).queries[queryId];
if (!_this.observableQueries[queryId].observableQuery.options.noFetch) {
_this.observableQueries[queryId].observableQuery.refetch();
}
});
};
QueryManager.prototype.startQuery = function (queryId, options, listener) {
this.addQueryListener(queryId, listener);
this.fetchQuery(queryId, options)
.catch(function (error) { return undefined; });
return queryId;
};
QueryManager.prototype.startGraphQLSubscription = function (options) {
var _this = this;
var document = options.document, variables = options.variables;
var transformedDoc = document;
if (this.addTypename) {
transformedDoc = addTypenameToDocument(transformedDoc);
}
var request = {
query: transformedDoc,
variables: variables,
operationName: getOperationName(transformedDoc),
};
var subId;
var observers = [];
return new Observable(function (observer) {
observers.push(observer);
if (observers.length === 1) {
var handler = function (error, result) {
if (error) {
observers.forEach(function (obs) {
if (obs.error) {
obs.error(error);
}
});
}
else {
_this.store.dispatch({
type: 'APOLLO_SUBSCRIPTION_RESULT',
document: transformedDoc,
operationName: getOperationName(transformedDoc),
result: { data: result },
variables: variables || {},
subscriptionId: subId,
extraReducers: _this.getExtraReducers(),
});
observers.forEach(function (obs) {
if (obs.next) {
obs.next(result);
}
});
}
};
subId = _this.networkInterface.subscribe(request, handler);
}
return {
unsubscribe: function () {
observers = observers.filter(function (obs) { return obs !== observer; });
if (observers.length === 0) {
_this.networkInterface.unsubscribe(subId);
}
},
_networkSubscriptionId: subId,
};
});
};
;
QueryManager.prototype.stopQuery = function (queryId) {
delete this.queryListeners[queryId];
delete this.queryDocuments[queryId];
this.stopQueryInStore(queryId);
};
QueryManager.prototype.getCurrentQueryResult = function (observableQuery, isOptimistic) {
if (isOptimistic === void 0) { isOptimistic = false; }
var _a = this.getQueryParts(observableQuery), variables = _a.variables, document = _a.document;
var lastResult = observableQuery.getLastResult();
var queryOptions = observableQuery.options;
var readOptions = {
store: isOptimistic ? this.getDataWithOptimisticResults() : this.getApolloState().data,
query: document,
variables: variables,
returnPartialData: false,
config: this.reducerConfig,
previousResult: lastResult ? lastResult.data : undefined,
};
try {
var data = readQueryFromStore(readOptions);
return maybeDeepFreeze({ data: data, partial: false });
}
catch (e) {
if (queryOptions.returnPartialData || queryOptions.noFetch) {
try {
readOptions.returnPartialData = true;
var data = readQueryFromStore(readOptions);
return { data: data, partial: true };
}
catch (e) {
}
}
return maybeDeepFreeze({ data: {}, partial: true });
}
};
QueryManager.prototype.getQueryWithPreviousResult = function (queryIdOrObservable, isOptimistic) {
if (isOptimistic === void 0) { isOptimistic = false; }
var observableQuery;
if (typeof queryIdOrObservable === 'string') {
if (!this.observableQueries[queryIdOrObservable]) {
throw new Error("ObservableQuery with this id doesn't exist: " + queryIdOrObservable);
}
observableQuery = this.observableQueries[queryIdOrObservable].observableQuery;
}
else {
observableQuery = queryIdOrObservable;
}
var _a = this.getQueryParts(observableQuery), variables = _a.variables, document = _a.document;
var data = this.getCurrentQueryResult(observableQuery, isOptimistic).data;
return {
previousResult: data,
variables: variables,
document: document,
};
};
QueryManager.prototype.transformResult = function (result) {
if (!this.resultTransformer) {
return result;
}
else {
return this.resultTransformer(result);
}
};
QueryManager.prototype.getQueryParts = function (observableQuery) {
var queryOptions = observableQuery.options;
var transformedDoc = observableQuery.options.query;
if (this.addTypename) {
transformedDoc = addTypenameToDocument(transformedDoc);
}
return {
variables: queryOptions.variables,
document: transformedDoc,
};
};
QueryManager.prototype.transformQueryDocument = function (options) {
var queryDoc = options.query;
if (this.addTypename) {
queryDoc = addTypenameToDocument(queryDoc);
}
return {
queryDoc: queryDoc,
};
};
QueryManager.prototype.getExtraReducers = function () {
var _this = this;
return Object.keys(this.observableQueries).map(function (obsQueryId) {
var queryOptions = _this.observableQueries[obsQueryId].observableQuery.options;
if (queryOptions.reducer) {
return createStoreReducer(queryOptions.reducer, queryOptions.query, queryOptions.variables || {}, _this.reducerConfig);
}
return null;
}).filter(function (reducer) { return reducer !== null; });
};
QueryManager.prototype.fetchRequest = function (_a) {
var _this = this;
var requestId = _a.requestId, queryId = _a.queryId, document = _a.document, options = _a.options;
var variables = options.variables, noFetch = options.noFetch, returnPartialData = options.returnPartialData;
var request = {
query: document,
variables: variables,
operationName: getOperationName(document),
};
var retPromise = new Promise(function (resolve, reject) {
_this.addFetchQueryPromise(requestId, retPromise, resolve, reject);
_this.deduplicator.query(request, _this.queryDeduplication)
.then(function (result) {
var extraReducers = _this.getExtraReducers();
_this.store.dispatch({
type: 'APOLLO_QUERY_RESULT',
document: document,
operationName: getOperationName(document),
result: result,
queryId: queryId,
requestId: requestId,
extraReducers: extraReducers,
});
_this.removeFetchQueryPromise(requestId);
if (result.errors) {
throw new ApolloError({
graphQLErrors: result.errors,
});
}
return result;
}).then(function () {
var resultFromStore;
try {
resultFromStore = readQueryFromStore({
store: _this.getApolloState().data,
variables: variables,
returnPartialData: returnPartialData || noFetch,
query: document,
config: _this.reducerConfig,
});
}
catch (e) { }
var reducerError = _this.getApolloState().reducerError;
if (!resultFromStore && reducerError) {
return Promise.reject(reducerError);
}
_this.removeFetchQueryPromise(requestId);
resolve({ data: resultFromStore, loading: false, networkStatus: NetworkStatus.ready });
return null;
}).catch(function (error) {
if (isApolloError(error)) {
reject(error);
}
else {
_this.store.dispatch({
type: 'APOLLO_QUERY_ERROR',
error: error,
queryId: queryId,
requestId: requestId,
});
_this.removeFetchQueryPromise(requestId);
reject(new ApolloError({
networkError: error,
}));
}
});
});
return retPromise;
};
QueryManager.prototype.refetchQueryByName = function (queryName) {
var _this = this;
var refetchedQueries = this.queryIdsByName[queryName];
if (refetchedQueries === undefined) {
console.warn("Warning: unknown query with name " + queryName + " asked to refetch");
}
else {
refetchedQueries.forEach(function (queryId) {
_this.observableQueries[queryId].observableQuery.refetch();
});
}
};
QueryManager.prototype.broadcastQueries = function () {
var _this = this;
var queries = this.getApolloState().queries;
Object.keys(this.queryListeners).forEach(function (queryId) {
var listeners = _this.queryListeners[queryId];
if (listeners) {
listeners.forEach(function (listener) {
if (listener) {
var queryStoreValue = queries[queryId];
listener(queryStoreValue);
}
});
}
});
};
QueryManager.prototype.generateRequestId = function () {
var requestId = this.idCounter;
this.idCounter++;
return requestId;
};
return QueryManager;
}());
export { QueryManager };
//# sourceMappingURL=QueryManager.js.map