diff --git a/lib/batch.js b/lib/batch.js index c9e07b0..9e3f84d 100644 --- a/lib/batch.js +++ b/lib/batch.js @@ -18,48 +18,33 @@ module.exports.config = function (settings) { return { handler: function (request, reply) { + const requests = []; + const payloads = []; const resultsData = { results: [], resultsMap: [] }; - const requests = []; - const requestRegex = /(?:\/)(?:\$(\d)+\.)?([^\/\$]*)/g; // /project/$1.project/tasks, does not allow using array responses - - const payloads = []; - const payloadRegex = /^\$(\d+)(?:\.([^\s\$]*))?/; - - // Validate requests - let errorMessage = null; - let i = 0; - let requestParts; - let result; - const il = request.payload.requests.length; + request.payload.requests.every((req, idx) => { - const parseRequest = function ($0, $1, $2) { + const requestParts = []; + const result = req.path.replace(internals.requestRegex, (match, p1, p2) => { - if ($1) { - if ($1 < i) { - requestParts.push({ type: 'ref', index: $1, value: $2 }); + if (!p1) { + requestParts.push({ type: 'text', value: p2 }); return ''; } - errorMessage = 'Request reference is beyond array size: ' + i; - return $0; - } - - requestParts.push({ type: 'text', value: $2 }); - return ''; - }; - - for ( ; i < il; ++i) { - - // Break into parts + if (p1 < idx) { + requestParts.push({ type: 'ref', index: p1, value: p2 }); + return ''; + } - requestParts = []; - result = request.payload.requests[i].path.replace(requestRegex, parseRequest); + errorMessage = 'Request reference is beyond array size: ' + idx; + return match; + }); // Make sure entire string was processed (empty) @@ -67,22 +52,21 @@ module.exports.config = function (settings) { requests.push(requestParts); } else { - errorMessage = errorMessage || 'Invalid request format in item: ' + i; - break; + errorMessage = errorMessage || 'Invalid request format in item: ' + idx; + return false; } - const payload = request.payload.requests[i].payload; - const payloadParts = internals.parsePayload(payloadRegex, payload); + const payloadParts = internals.parsePayload(req.payload); payloads.push(payloadParts || []); - } + return true; + }); - if (errorMessage === null) { - internals.process(request, requests, payloads, resultsData, reply); - } - else { - reply(Boom.badRequest(errorMessage)); + if (errorMessage !== null) { + return reply(Boom.badRequest(errorMessage)); } + + internals.process(request, requests, payloads, resultsData, reply); }, notes: settings.notes, description: settings.description, @@ -106,28 +90,21 @@ internals.process = function (request, requests, payloads, resultsData, reply) { const fnsParallel = []; const fnsSerial = []; - const callBatch = function (pos, requestParts, payloadParts) { + requests.forEach((requestParts, idx) => { - return (callback) => { - - internals.batch(request, resultsData, pos, requestParts, payloadParts, callback); - }; - }; + const payloadParts = payloads[idx]; + if (internals.hasRefPart(requestParts) || payloadParts.length) { + return fnsSerial.push((callback) => { - let i = 0; - const il = requests.length; + internals.batch(request, resultsData, idx, requestParts, payloadParts, callback); + }); + } - for ( ; i < il; ++i) { - const requestParts = requests[i]; - const payloadParts = payloads[i]; + fnsParallel.push((callback) => { - if (internals.hasRefPart(requestParts) || payloadParts.length) { - fnsSerial.push(callBatch(i, requestParts, payloadParts)); - } - else { - fnsParallel.push(callBatch(i, requestParts)); - } - } + internals.batch(request, resultsData, idx, requestParts, undefined, callback); + }); + }); Async.series([ (callback) => { @@ -152,62 +129,58 @@ internals.process = function (request, requests, payloads, resultsData, reply) { internals.hasRefPart = function (parts) { - let i = 0; - const il = parts.length; - for ( ; i < il; ++i) { - if (parts[i].type === 'ref') { - return true; - } - } - - return false; + return parts.some((part) => part.type === 'ref'); }; internals.buildPath = function (resultsData, pos, parts) { let path = ''; let error = null; - let i = 0; - const il = parts.length; + let ref; + let value; + let part; + const partsLength = parts.length; - for ( ; i < il; ++i) { + for (let i = 0; i < partsLength; ++i) { path += '/'; + part = parts[i]; - if (parts[i].type === 'ref') { - const ref = resultsData.resultsMap[parts[i].index]; + if (part.type !== 'ref') { + path += part.value; + continue; + } - if (ref) { - const value = Hoek.reach(ref, parts[i].value); + ref = resultsData.resultsMap[part.index]; - if (value !== null && value !== undefined) { + if (!ref) { + error = new Error('Missing reference response'); + break; + } - if (/^[\w:]+$/.test(value)) { - path += value; - } - else { - error = new Error('Reference value includes illegal characters'); - break; - } - } - else { - error = new Error('Reference not found'); - break; - } - } - else { - error = new Error('Missing reference response'); - break; - } + value = Hoek.reach(ref, part.value); + + if (value === null || value === undefined) { + error = new Error('Reference not found'); + break; } - else { - path += parts[i].value; + + if (!/^[\w:]+$/.test(value)) { + error = new Error('Reference value includes illegal characters'); + break; } + + path += value; + } return error ? error : path; }; -internals.parsePayload = function (re, obj) { +internals.payloadRegex = /^\$(\d+)(?:\.([^\s\$]*))?/; + +internals.requestRegex = /(?:\/)(?:\$(\d)+\.)?([^\/\$]*)/g; + +internals.parsePayload = function (obj) { const payloadParts = []; @@ -217,16 +190,21 @@ internals.parsePayload = function (re, obj) { Traverse(obj).forEach(function (value) { - if (typeof value === 'string') { - const match = value.match(re); - if (match) { - payloadParts.push({ - path: this.path, - resultIndex: match[1], - resultPath: match[2] - }); - } + if (typeof value !== 'string') { + return; + } + + const match = internals.payloadRegex.exec(value); + + if (!match) { + return; } + + payloadParts.push({ + path: this.path, + resultIndex: match[1], + resultPath: match[2] + }); }); return payloadParts; @@ -234,28 +212,30 @@ internals.parsePayload = function (re, obj) { internals.evalResults = function (results, index, path) { - let result = results[index]; + const result = results[index]; - if (path) { - result = Hoek.reach(result, path) || {}; + if (!path) { + return result; } - return result; + return Hoek.reach(result, path) || {}; }; internals.buildPayload = function (payload, resultsData, parts) { - let i = 0; - const il = parts.length; - for ( ; i < il; ++i) { + const partsLength = parts.length; + let result; + let part; + for ( let i = 0; i < partsLength; ++i) { - const result = internals.evalResults(resultsData.resultsMap, parts[i].resultIndex, parts[i].resultPath); + part = parts[i]; + result = internals.evalResults(resultsData.resultsMap, part.resultIndex, part.resultPath); - if (parts[i].path.length) { - Traverse(payload).set(parts[i].path, result); + if (!part.path.length) { + payload = result; } else { - payload = result; + Traverse(payload).set(part.path, result); } } @@ -272,25 +252,26 @@ internals.batch = function (batchRequest, resultsData, pos, requestParts, payloa } // Make request - batchRequest.payload.requests[pos].path = path; + const request = batchRequest.payload.requests[pos]; + + request.path = path; if (payloadParts && payloadParts.length) { - const payload = internals.buildPayload( - batchRequest.payload.requests[pos].payload, + + // Make payload + request.payload = internals.buildPayload( + request.payload, resultsData, payloadParts ); - - // Make payload - batchRequest.payload.requests[pos].payload = payload; } - internals.dispatch(batchRequest, batchRequest.payload.requests[pos], (data) => { + internals.dispatch(batchRequest, request, (data) => { // If redirection if (('' + data.statusCode).indexOf('3') === 0) { - batchRequest.payload.requests[pos].path = data.headers.location; - internals.dispatch(batchRequest, batchRequest.payload.requests[pos], (batchData) => { + request.path = data.headers.location; + internals.dispatch(batchRequest, request, (batchData) => { const batchResult = batchData.result; @@ -311,17 +292,19 @@ internals.batch = function (batchRequest, resultsData, pos, requestParts, payloa internals.dispatch = function (batchRequest, request, callback) { - let path = request.path; + let path; if (request.query) { - const urlObject = { + path = Url.format({ pathname: request.path, query: request.query - }; - path = Url.format(urlObject); + }); + } + else { + path = request.path; } - const body = (request.payload !== null && request.payload !== undefined ? JSON.stringify(request.payload) : null); // payload can be '' or 0 + const body = (request.payload !== null && request.payload !== undefined ? JSON.stringify(request.payload) : null); const injectOptions = { url: path, method: request.method,