You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
9.6 KiB
187 lines
9.6 KiB
import { __assign, __extends, __rest } from "tslib"; |
|
import { ApolloLink } from "../core/index.js"; |
|
import { Observable, hasDirectives, removeClientSetsFromDocument, } from "../../utilities/index.js"; |
|
import { fromError } from "../utils/index.js"; |
|
import { serializeFetchParameter, selectURI, parseAndCheckHttpResponse, checkFetcher, selectHttpOptionsAndBodyInternal, defaultPrinter, fallbackHttpConfig, } from "../http/index.js"; |
|
import { BatchLink } from "../batch/index.js"; |
|
import { filterOperationVariables } from "../utils/filterOperationVariables.js"; |
|
/** |
|
* Transforms Operation for into HTTP results. |
|
* context can include the headers property, which will be passed to the fetch function |
|
*/ |
|
var BatchHttpLink = /** @class */ (function (_super) { |
|
__extends(BatchHttpLink, _super); |
|
function BatchHttpLink(fetchParams) { |
|
var _this = _super.call(this) || this; |
|
var _a = fetchParams || {}, _b = _a.uri, uri = _b === void 0 ? "/graphql" : _b, |
|
// use default global fetch if nothing is passed in |
|
fetcher = _a.fetch, _c = _a.print, print = _c === void 0 ? defaultPrinter : _c, includeExtensions = _a.includeExtensions, preserveHeaderCase = _a.preserveHeaderCase, batchInterval = _a.batchInterval, batchDebounce = _a.batchDebounce, batchMax = _a.batchMax, batchKey = _a.batchKey, _d = _a.includeUnusedVariables, includeUnusedVariables = _d === void 0 ? false : _d, requestOptions = __rest(_a, ["uri", "fetch", "print", "includeExtensions", "preserveHeaderCase", "batchInterval", "batchDebounce", "batchMax", "batchKey", "includeUnusedVariables"]); |
|
// dev warnings to ensure fetch is present |
|
checkFetcher(fetcher); |
|
//fetcher is set here rather than the destructuring to ensure fetch is |
|
//declared before referencing it. Reference in the destructuring would cause |
|
//a ReferenceError |
|
if (!fetcher) { |
|
fetcher = fetch; |
|
} |
|
var linkConfig = { |
|
http: { includeExtensions: includeExtensions, preserveHeaderCase: preserveHeaderCase }, |
|
options: requestOptions.fetchOptions, |
|
credentials: requestOptions.credentials, |
|
headers: requestOptions.headers, |
|
}; |
|
_this.batchDebounce = batchDebounce; |
|
_this.batchInterval = batchInterval || 10; |
|
_this.batchMax = batchMax || 10; |
|
var batchHandler = function (operations) { |
|
var chosenURI = selectURI(operations[0], uri); |
|
var context = operations[0].getContext(); |
|
var clientAwarenessHeaders = {}; |
|
if (context.clientAwareness) { |
|
var _a = context.clientAwareness, name_1 = _a.name, version = _a.version; |
|
if (name_1) { |
|
clientAwarenessHeaders["apollographql-client-name"] = name_1; |
|
} |
|
if (version) { |
|
clientAwarenessHeaders["apollographql-client-version"] = version; |
|
} |
|
} |
|
var contextConfig = { |
|
http: context.http, |
|
options: context.fetchOptions, |
|
credentials: context.credentials, |
|
headers: __assign(__assign({}, clientAwarenessHeaders), context.headers), |
|
}; |
|
var queries = operations.map(function (_a) { |
|
var query = _a.query; |
|
if (hasDirectives(["client"], query)) { |
|
return removeClientSetsFromDocument(query); |
|
} |
|
return query; |
|
}); |
|
// If we have a query that returned `null` after removing client-only |
|
// fields, it indicates a query that is using all client-only fields. |
|
if (queries.some(function (query) { return !query; })) { |
|
return fromError(new Error("BatchHttpLink: Trying to send a client-only query to the server. To send to the server, ensure a non-client field is added to the query or enable the `transformOptions.removeClientFields` option.")); |
|
} |
|
//uses fallback, link, and then context to build options |
|
var optsAndBody = operations.map(function (operation, index) { |
|
var result = selectHttpOptionsAndBodyInternal(__assign(__assign({}, operation), { query: queries[index] }), print, fallbackHttpConfig, linkConfig, contextConfig); |
|
if (result.body.variables && !includeUnusedVariables) { |
|
result.body.variables = filterOperationVariables(result.body.variables, operation.query); |
|
} |
|
return result; |
|
}); |
|
var loadedBody = optsAndBody.map(function (_a) { |
|
var body = _a.body; |
|
return body; |
|
}); |
|
var options = optsAndBody[0].options; |
|
// There's no spec for using GET with batches. |
|
if (options.method === "GET") { |
|
return fromError(new Error("apollo-link-batch-http does not support GET requests")); |
|
} |
|
try { |
|
options.body = serializeFetchParameter(loadedBody, "Payload"); |
|
} |
|
catch (parseError) { |
|
return fromError(parseError); |
|
} |
|
var controller; |
|
if (!options.signal && typeof AbortController !== "undefined") { |
|
controller = new AbortController(); |
|
options.signal = controller.signal; |
|
} |
|
return new Observable(function (observer) { |
|
fetcher(chosenURI, options) |
|
.then(function (response) { |
|
// Make the raw response available in the context. |
|
operations.forEach(function (operation) { |
|
return operation.setContext({ response: response }); |
|
}); |
|
return response; |
|
}) |
|
.then(parseAndCheckHttpResponse(operations)) |
|
.then(function (result) { |
|
controller = undefined; |
|
// we have data and can send it to back up the link chain |
|
observer.next(result); |
|
observer.complete(); |
|
return result; |
|
}) |
|
.catch(function (err) { |
|
controller = undefined; |
|
// if it is a network error, BUT there is graphql result info |
|
// fire the next observer before calling error |
|
// this gives apollo-client (and react-apollo) the `graphqlErrors` and `networkErrors` |
|
// to pass to UI |
|
// this should only happen if we *also* have data as part of the response key per |
|
// the spec |
|
if (err.result && err.result.errors && err.result.data) { |
|
// if we dont' call next, the UI can only show networkError because AC didn't |
|
// get andy graphqlErrors |
|
// this is graphql execution result info (i.e errors and possibly data) |
|
// this is because there is no formal spec how errors should translate to |
|
// http status codes. So an auth error (401) could have both data |
|
// from a public field, errors from a private field, and a status of 401 |
|
// { |
|
// user { // this will have errors |
|
// firstName |
|
// } |
|
// products { // this is public so will have data |
|
// cost |
|
// } |
|
// } |
|
// |
|
// the result of above *could* look like this: |
|
// { |
|
// data: { products: [{ cost: "$10" }] }, |
|
// errors: [{ |
|
// message: 'your session has timed out', |
|
// path: [] |
|
// }] |
|
// } |
|
// status code of above would be a 401 |
|
// in the UI you want to show data where you can, errors as data where you can |
|
// and use correct http status codes |
|
observer.next(err.result); |
|
} |
|
observer.error(err); |
|
}); |
|
return function () { |
|
// XXX support canceling this request |
|
// https://developers.google.com/web/updates/2017/09/abortable-fetch |
|
if (controller) |
|
controller.abort(); |
|
}; |
|
}); |
|
}; |
|
batchKey = |
|
batchKey || |
|
(function (operation) { |
|
var context = operation.getContext(); |
|
var contextConfig = { |
|
http: context.http, |
|
options: context.fetchOptions, |
|
credentials: context.credentials, |
|
headers: context.headers, |
|
}; |
|
//may throw error if config not serializable |
|
return selectURI(operation, uri) + JSON.stringify(contextConfig); |
|
}); |
|
_this.batcher = new BatchLink({ |
|
batchDebounce: _this.batchDebounce, |
|
batchInterval: _this.batchInterval, |
|
batchMax: _this.batchMax, |
|
batchKey: batchKey, |
|
batchHandler: batchHandler, |
|
}); |
|
return _this; |
|
} |
|
BatchHttpLink.prototype.request = function (operation) { |
|
return this.batcher.request(operation); |
|
}; |
|
return BatchHttpLink; |
|
}(ApolloLink)); |
|
export { BatchHttpLink }; |
|
//# sourceMappingURL=batchHttpLink.js.map
|