Skip to content

Commit

Permalink
[BUGFIX fetchOptions] fix fetchOptions to support POST body of all va…
Browse files Browse the repository at this point in the history
…lid types (#6562)
  • Loading branch information
nlfurniss authored and runspired committed Oct 16, 2019
1 parent a6bda60 commit e9d5462
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { module, test } from 'qunit';
import { fetchOptions } from '@ember-data/adapter/rest';

module('unit/adapters/rest-adapter/fetch-options', function(hooks) {
test("fetchOptions removes undefined query params when method is POST and 'data' is an object", function(assert) {
assert.expect(1);

const dataAsObject = {
a: 1,
b: undefined,
c: 3,
d: null,
e: 0,
f: false,
};

const undefinedQueryStringOptions = {
url: 'https://emberjs.com',
method: 'POST',
data: dataAsObject,
};

let options = fetchOptions(undefinedQueryStringOptions);
assert.deepEqual(options.body, '{"a":1,"c":3,"d":null,"e":0,"f":false}');
});

test('fetchOptions sets the request body correctly when the method is not GET or HEAD', function(assert) {
assert.expect(3);

const baseOptions = {
url: '/',
method: 'POST',
data: { a: 1 },
};

// Tests POST method.
let options = fetchOptions(baseOptions);
assert.equal(options.body, JSON.stringify(baseOptions.data), 'POST request body correctly set');

// Tests PUT method.
baseOptions.method = 'PUT';
options = fetchOptions(baseOptions);
assert.equal(options.body, JSON.stringify(baseOptions.data), 'PUT request body correctly set');

// Tests DELETE method.
baseOptions.method = 'DELETE';
options = fetchOptions(baseOptions);
assert.equal(options.body, JSON.stringify(baseOptions.data), 'DELETE request has the correct body');
});

test("fetchOptions sets the request body correctly when the method is POST and 'data' is a string", function(assert) {
assert.expect(1);

// Tests stringified objects.
const stringifiedData = JSON.stringify({ a: 1, b: 2 });
const optionsWithStringData = {
url: 'https://emberjs.com',
method: 'POST',
data: stringifiedData,
};

let options = fetchOptions(optionsWithStringData);
assert.equal(options.body, stringifiedData);
});

test('fetchOptions does not set a request body when the method is GET or HEAD', function(assert) {
assert.expect(4);

const baseOptions = {
url: '/',
method: 'GET',
data: { a: 1 },
};

let options = fetchOptions(baseOptions);
assert.strictEqual(options.body, undefined, 'GET request does not have a request body');

baseOptions.method = 'HEAD';
options = fetchOptions(baseOptions);
assert.strictEqual(options.body, undefined, 'HEAD request does not have a request body');

baseOptions.data = {};
options = fetchOptions(baseOptions);
assert.strictEqual(
options.body,
undefined,
'HEAD request does not have a request body when `data` is an empty object'
);

baseOptions.method = 'GET';
options = fetchOptions(baseOptions);
assert.strictEqual(
options.body,
undefined,
'GET request does not have a request body when `data` is an empty object'
);
});

test("fetchOptions correctly processes an empty 'data' object", function(assert) {
assert.expect(2);

const getData = {
url: 'https://emberjs.com',
method: 'GET',
data: {},
};

const getOptions = fetchOptions(getData);
assert.equal(getOptions.url.indexOf('?'), -1, 'A question mark is not added if there are no query params to add');

const postData = {
url: 'https://emberjs.com',
method: 'POST',
data: {},
};

const postOptions = fetchOptions(postData);
assert.equal(postOptions.body, '{}', "'options.body' is an empty object");
});

test("fetchOptions sets the request body correctly when 'data' is FormData", function(assert) {
assert.expect(1);

const formData = new FormData();
const postData = {
url: 'https://emberjs.com',
method: 'POST',
data: formData,
};

const postOptions = fetchOptions(postData);
assert.strictEqual(postOptions.body, formData, "'options.body' is the FormData passed in");
});

test("fetchOptions sets the request body correctly when 'data' is a String", function(assert) {
assert.expect(1);

let stringBody = JSON.stringify({ a: 1, b: 2, c: 3 });
const postData = {
url: 'https://emberjs.com',
method: 'POST',
data: stringBody,
};

const postOptions = fetchOptions(postData);
assert.equal(postOptions.body, stringBody, "'options.body' is the String passed in");
});
});
14 changes: 13 additions & 1 deletion packages/adapter/addon/rest.js
Original file line number Diff line number Diff line change
Expand Up @@ -1363,7 +1363,19 @@ export function fetchOptions(options, adapter) {
} else {
// NOTE: a request's body cannot be an object, so we stringify it if it is.
// JSON.stringify removes keys with values of `undefined` (mimics jQuery.ajax).
options.body = JSON.stringify(options.data);
// If the data is not a POJO (it's a String, FormData, etc), we just set it.
// If the data is a string, we assume it's a stringified object.

/* We check for Objects this way because we want the logic inside the consequent to run
* if `options.data` is a POJO, not if it is a data structure whose `typeof` returns "object"
* when it's not (Array, FormData, etc). The reason we don't use `options.data.constructor`
* to check is in case `data` is an object with no prototype (e.g. created with null).
*/
if (Object.prototype.toString.call(options.data) === '[object Object]') {
options.body = JSON.stringify(options.data);
} else {
options.body = options.data;
}
}
}

Expand Down

0 comments on commit e9d5462

Please sign in to comment.