(router, req)
| 64 | // Returns a promise for a {response} object. |
| 65 | // TODO: pass along auth correctly |
| 66 | async function handleBatch(router, req) { |
| 67 | if (!Array.isArray(req.body?.requests)) { |
| 68 | throw new Parse.Error(Parse.Error.INVALID_JSON, 'requests must be an array'); |
| 69 | } |
| 70 | const batchRequestLimit = req.config?.requestComplexity?.batchRequestLimit ?? -1; |
| 71 | if (batchRequestLimit > -1 && !req.auth?.isMaster && !req.auth?.isMaintenance && req.body.requests.length > batchRequestLimit) { |
| 72 | throw new Parse.Error( |
| 73 | Parse.Error.INVALID_JSON, |
| 74 | `Batch request contains ${req.body.requests.length} sub-requests, which exceeds the limit of ${batchRequestLimit}.` |
| 75 | ); |
| 76 | } |
| 77 | for (const restRequest of req.body.requests) { |
| 78 | if (!restRequest || typeof restRequest !== 'object' || typeof restRequest.path !== 'string') { |
| 79 | throw new Parse.Error(Parse.Error.INVALID_JSON, 'batch request path must be a string'); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | // The batch paths are all from the root of our domain. |
| 84 | // That means they include the API prefix, that the API is mounted |
| 85 | // to. However, our promise router does not route the api prefix. So |
| 86 | // we need to figure out the API prefix, so that we can strip it |
| 87 | // from all the subrequests. |
| 88 | if (!req.originalUrl.endsWith(batchPath)) { |
| 89 | throw 'internal routing problem - expected url to end with batch'; |
| 90 | } |
| 91 | |
| 92 | const makeRoutablePath = makeBatchRoutingPathFunction( |
| 93 | req.originalUrl, |
| 94 | req.config.serverURL, |
| 95 | req.config.publicServerURL |
| 96 | ); |
| 97 | |
| 98 | // Enforce rate limits for each batch sub-request by invoking the |
| 99 | // rate limit handler. This ensures sub-requests consume tokens from |
| 100 | // the same window state as direct requests. |
| 101 | const rateLimits = req.config.rateLimits || []; |
| 102 | for (const restRequest of req.body.requests) { |
| 103 | const routablePath = makeRoutablePath(restRequest.path); |
| 104 | if ((restRequest.method || 'GET').toUpperCase() === 'POST' && routablePath === batchPath) { |
| 105 | throw new Parse.Error(Parse.Error.INVALID_JSON, 'nested batch requests are not allowed'); |
| 106 | } |
| 107 | for (const limit of rateLimits) { |
| 108 | const pathExp = limit.path.regexp || limit.path; |
| 109 | if (!pathExp.test(routablePath)) { |
| 110 | continue; |
| 111 | } |
| 112 | const info = { ...req.info }; |
| 113 | if (routablePath === '/login') { |
| 114 | delete info.sessionToken; |
| 115 | } |
| 116 | const fakeReq = { |
| 117 | ip: req.ip || req.config?.ip || '127.0.0.1', |
| 118 | method: (restRequest.method || 'GET').toUpperCase(), |
| 119 | _batchOriginalMethod: 'POST', |
| 120 | config: req.config, |
| 121 | auth: req.auth, |
| 122 | info, |
| 123 | }; |
no test coverage detected