123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- "use strict";
- const Parse = require('parse/node').Parse;
- const path = require('path');
- const batchPath = '/batch';
- function mountOnto(router) {
- router.route('POST', batchPath, req => {
- return handleBatch(router, req);
- });
- }
- function parseURL(urlString) {
- try {
- return new URL(urlString);
- } catch (error) {
- return undefined;
- }
- }
- function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) {
- serverURL = serverURL ? parseURL(serverURL) : undefined;
- publicServerURL = publicServerURL ? parseURL(publicServerURL) : undefined;
- const apiPrefixLength = originalUrl.length - batchPath.length;
- let apiPrefix = originalUrl.slice(0, apiPrefixLength);
- const makeRoutablePath = function (requestPath) {
-
- if (requestPath.slice(0, apiPrefix.length) != apiPrefix) {
- throw new Parse.Error(Parse.Error.INVALID_JSON, 'cannot route batch path ' + requestPath);
- }
- return path.posix.join('/', requestPath.slice(apiPrefix.length));
- };
- if (serverURL && publicServerURL && serverURL.pathname != publicServerURL.pathname) {
- const localPath = serverURL.pathname;
- const publicPath = publicServerURL.pathname;
-
- apiPrefix = localPath;
- return function (requestPath) {
-
-
- const startsWithLocal = requestPath.startsWith(localPath);
- const startsWithPublic = requestPath.startsWith(publicPath);
- const pathLengthToUse = startsWithLocal && startsWithPublic ? Math.max(localPath.length, publicPath.length) : startsWithLocal ? localPath.length : publicPath.length;
- const newPath = path.posix.join('/', localPath, '/', requestPath.slice(pathLengthToUse));
-
- return makeRoutablePath(newPath);
- };
- }
- return makeRoutablePath;
- }
- function handleBatch(router, req) {
- if (!Array.isArray(req.body.requests)) {
- throw new Parse.Error(Parse.Error.INVALID_JSON, 'requests must be an array');
- }
-
-
-
-
-
- if (!req.originalUrl.endsWith(batchPath)) {
- throw 'internal routing problem - expected url to end with batch';
- }
- const makeRoutablePath = makeBatchRoutingPathFunction(req.originalUrl, req.config.serverURL, req.config.publicServerURL);
- const batch = transactionRetries => {
- let initialPromise = Promise.resolve();
- if (req.body.transaction === true) {
- initialPromise = req.config.database.createTransactionalSession();
- }
- return initialPromise.then(() => {
- const promises = req.body.requests.map(restRequest => {
- const routablePath = makeRoutablePath(restRequest.path);
-
- const request = {
- body: restRequest.body,
- config: req.config,
- auth: req.auth,
- info: req.info
- };
- return router.tryRouteRequest(restRequest.method, routablePath, request).then(response => {
- return {
- success: response.response
- };
- }, error => {
- return {
- error: {
- code: error.code,
- error: error.message
- }
- };
- });
- });
- return Promise.all(promises).then(results => {
- if (req.body.transaction === true) {
- if (results.find(result => typeof result.error === 'object')) {
- return req.config.database.abortTransactionalSession().then(() => {
- return Promise.reject({
- response: results
- });
- });
- } else {
- return req.config.database.commitTransactionalSession().then(() => {
- return {
- response: results
- };
- });
- }
- } else {
- return {
- response: results
- };
- }
- }).catch(error => {
- if (error && error.response && error.response.find(errorItem => typeof errorItem.error === 'object' && errorItem.error.code === 251) && transactionRetries > 0) {
- return batch(transactionRetries - 1);
- }
- throw error;
- });
- });
- };
- return batch(5);
- }
- module.exports = {
- mountOnto,
- makeBatchRoutingPathFunction
- };
|