diff --git a/spec/HTTPRequest.spec.js b/spec/HTTPRequest.spec.js new file mode 100644 index 00000000..01efe42b --- /dev/null +++ b/spec/HTTPRequest.spec.js @@ -0,0 +1,181 @@ +var httpRequest = require("../src/httpRequest"), + bodyParser = require('body-parser'), + express = require("express"); + +var port = 13371; +var httpRequestServer = "http://localhost:"+port; + +var app = express(); +app.use(bodyParser.json({ 'type': '*/*' })); +app.get("/hello", function(req, res){ + res.json({response: "OK"}); +}); + +app.get("/404", function(req, res){ + res.status(404); + res.send("NO"); +}); + +app.get("/301", function(req, res){ + res.status(301); + res.location("/hello"); + res.send(); +}); + +app.post('/echo', function(req, res){ + res.json(req.body); +}) + +app.listen(13371); + + +describe("httpRequest", () => { + + it("should do /hello", (done) => { + httpRequest({ + url: httpRequestServer+"/hello" + }).then(function(httpResponse){ + expect(httpResponse.status).toBe(200); + expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}')); + expect(httpResponse.text).toEqual('{"response":"OK"}'); + expect(httpResponse.data.response).toEqual("OK"); + done(); + }, function(){ + fail("should not fail"); + done(); + }) + }); + + it("should do /hello with callback and promises", (done) => { + var calls = 0; + httpRequest({ + url: httpRequestServer+"/hello", + success: function() { calls++; }, + error: function() { calls++; } + }).then(function(httpResponse){ + expect(calls).toBe(1); + expect(httpResponse.status).toBe(200); + expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}')); + expect(httpResponse.text).toEqual('{"response":"OK"}'); + expect(httpResponse.data.response).toEqual("OK"); + done(); + }, function(){ + fail("should not fail"); + done(); + }) + }); + + it("should do not follow redirects by default", (done) => { + + httpRequest({ + url: httpRequestServer+"/301" + }).then(function(httpResponse){ + expect(httpResponse.status).toBe(301); + done(); + }, function(){ + fail("should not fail"); + done(); + }) + }); + + it("should follow redirects when set", (done) => { + + httpRequest({ + url: httpRequestServer+"/301", + followRedirects: true + }).then(function(httpResponse){ + expect(httpResponse.status).toBe(200); + expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}')); + expect(httpResponse.text).toEqual('{"response":"OK"}'); + expect(httpResponse.data.response).toEqual("OK"); + done(); + }, function(){ + fail("should not fail"); + done(); + }) + }); + + it("should fail on 404", (done) => { + var calls = 0; + httpRequest({ + url: httpRequestServer+"/404", + success: function() { + calls++; + fail("should not succeed"); + done(); + }, + error: function(httpResponse) { + calls++; + expect(calls).toBe(1); + expect(httpResponse.status).toBe(404); + expect(httpResponse.buffer).toEqual(new Buffer('NO')); + expect(httpResponse.text).toEqual('NO'); + expect(httpResponse.data).toBe(undefined); + done(); + } + }); + }) + + it("should fail on 404", (done) => { + httpRequest({ + url: httpRequestServer+"/404", + }).then(function(httpResponse){ + fail("should not succeed"); + done(); + }, function(httpResponse){ + expect(httpResponse.status).toBe(404); + expect(httpResponse.buffer).toEqual(new Buffer('NO')); + expect(httpResponse.text).toEqual('NO'); + expect(httpResponse.data).toBe(undefined); + done(); + }) + }) + + it("should post on echo", (done) => { + var calls = 0; + httpRequest({ + method: "POST", + url: httpRequestServer+"/echo", + body: { + foo: "bar" + }, + headers: { + 'Content-Type': 'application/json' + }, + success: function() { calls++; }, + error: function() { calls++; } + }).then(function(httpResponse){ + expect(calls).toBe(1); + expect(httpResponse.status).toBe(200); + expect(httpResponse.data).toEqual({foo: "bar"}); + done(); + }, function(httpResponse){ + fail("should not fail"); + done(); + }) + }); + it("should encode a JSON body", (done) => { + + var result = httpRequest.encodeBody({"foo": "bar"}, {'Content-Type': 'application/json'}); + expect(result).toEqual('{"foo":"bar"}'); + done(); + + }) + it("should encode a www-form body", (done) => { + + var result = httpRequest.encodeBody({"foo": "bar", "bar": "baz"}, {'cOntent-tYpe': 'application/x-www-form-urlencoded'}); + expect(result).toEqual("foo=bar&bar=baz"); + done(); + }); + it("should not encode a wrong content type", (done) => { + + var result = httpRequest.encodeBody({"foo": "bar", "bar": "baz"}, {'cOntent-tYpe': 'mime/jpeg'}); + expect(result).toEqual({"foo": "bar", "bar": "baz"}); + done(); + }); + it("should not encode when missing content type", (done) => { + var result = httpRequest.encodeBody({"foo": "bar", "bar": "baz"}, {'X-Custom-Header': 'my-header'}); + expect(result).toEqual({"foo": "bar", "bar": "baz"}); + done(); + }) +}); diff --git a/src/httpRequest.js b/src/httpRequest.js index db696c65..2b5f9bff 100644 --- a/src/httpRequest.js +++ b/src/httpRequest.js @@ -1,6 +1,27 @@ var request = require("request"), Parse = require('parse/node').Parse; +var encodeBody = function(body, headers = {}) { + if (typeof body !== 'object') { + return body; + } + var contentTypeKeys = Object.keys(headers).filter((key) => { + return key.match(/content-type/i) != null; + }); + + if (contentTypeKeys.length == 1) { + var contentType = contentTypeKeys[0]; + if (headers[contentType].match(/application\/json/i)) { + body = JSON.stringify(body); + } else if(headers[contentType].match(/application\/x-www-form-urlencoded/i)) { + body = Object.keys(body).map(function(key){ + return `${key}=${encodeURIComponent(body[key])}` + }).join("&"); + } + } + return body; +} + module.exports = function(options) { var promise = new Parse.Promise(); var callbacks = { @@ -9,13 +30,11 @@ module.exports = function(options) { }; delete options.success; delete options.error; - if (options.uri && !options.url) { - options.uri = options.url; - delete options.url; - } - if (typeof options.body === 'object') { - options.body = JSON.stringify(options.body); - } + delete options.uri; // not supported + options.body = encodeBody(options.body, options.headers); + // set follow redirects to false by default + options.followRedirect = options.followRedirects == true; + request(options, (error, response, body) => { var httpResponse = {}; httpResponse.status = response.statusCode; @@ -29,15 +48,17 @@ module.exports = function(options) { // Consider <200 && >= 400 as errors if (error || httpResponse.status <200 || httpResponse.status >=400) { if (callbacks.error) { - return callbacks.error(httpResponse); + callbacks.error(httpResponse); } return promise.reject(httpResponse); } else { if (callbacks.success) { - return callbacks.success(httpResponse); + callbacks.success(httpResponse); } return promise.resolve(httpResponse); } }); return promise; -}; \ No newline at end of file +}; + +module.exports.encodeBody = encodeBody;