Skip to content

Commit

Permalink
Deprecate options.query in favor of options.searchParams (#697)
Browse files Browse the repository at this point in the history
  • Loading branch information
szmarczak authored and sindresorhus committed Jan 17, 2019
1 parent 2fe5bd5 commit b223663
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 36 deletions.
4 changes: 2 additions & 2 deletions migration-guides.md
Expand Up @@ -56,7 +56,7 @@ Oh, and one more thing... There's no `time` option. Assume [it's always true](ht

Readability is very important to us, so we have different names for these options:

- `qs`[`query`](https://github.com/sindresorhus/got#query)
- `qs`[`searchParams`](https://github.com/sindresorhus/got#searchParams)
- `strictSSL`[`rejectUnauthorized`](https://github.com/sindresorhus/got#rejectUnauthorized)
- `gzip`[`decompress`](https://github.com/sindresorhus/got#decompress)
- `jar`[`cookieJar`](https://github.com/sindresorhus/got#cookiejar) (accepts [`tough-cookie`](https://github.com/salesforce/tough-cookie) jar)
Expand All @@ -67,7 +67,7 @@ It's more clear, isn't it?

The [`timeout` option](https://github.com/sindresorhus/got#timeout) has some extra features. You can [set timeouts on particular events](readme.md#timeout)!

The [`query` option](https://github.com/sindresorhus/got#query) is always serialized using [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) unless it's a `string`.
The [`searchParams` option](https://github.com/sindresorhus/got#searchParams) is always serialized using [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) unless it's a `string`.

The [`baseUrl` option](https://github.com/sindresorhus/got#baseurl) appends the ending slash if it's not present.

Expand Down
16 changes: 9 additions & 7 deletions readme.md
Expand Up @@ -202,22 +202,24 @@ If set to `true` and `Content-Type` header is not set, it will be set to `applic

Parse response body with `JSON.parse` and set `accept` header to `application/json`. If used in conjunction with the `form` option, the `body` will the stringified as querystring and the response parsed as JSON.

###### query
###### searchParams

Type: `string` `Object<string, string|number>` [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)

**Note**: The `query` option was renamed to `searchParams` in Got v10. The `query` option name is still functional, but is being deprecated and will be completely removed in Got v11.

Query string that will be added to the request URL. This will override the query string in `url`.

If you need to pass in an array, you can do it using a `URLSearchParams` instance:

```js
const got = require('got');

const query = new URLSearchParams([['key', 'a'], ['key', 'b']]);
const searchParams = new URLSearchParams([['key', 'a'], ['key', 'b']]);

got('https://example.com', {query});
got('https://example.com', {searchParams});

console.log(query.toString());
console.log(searchParams.toString());
//=> 'key=a&key=b'
```

Expand All @@ -227,11 +229,11 @@ And if you need a different array format, you could use the [`query-string`](htt
const got = require('got');
const queryString = require('query-string');

const query = queryString.stringify({key: ['a', 'b']}, {arrayFormat: 'bracket'});
const searchParams = queryString.stringify({key: ['a', 'b']}, {arrayFormat: 'bracket'});

got('https://example.com', {query});
got('https://example.com', {searchParams});

console.log(query);
console.log(searchParams);
//=> 'key[]=a&key[]=b'
```

Expand Down
32 changes: 24 additions & 8 deletions source/normalize-arguments.js
Expand Up @@ -12,6 +12,8 @@ const knownHookEvents = require('./known-hook-events');

const retryAfterStatusCodes = new Set([413, 429, 503]);

let shownDeprecation = false;

// `preNormalize` handles static options (e.g. headers).
// For example, when you create a custom instance and make a request
// with no static changes, they won't be normalized again.
Expand Down Expand Up @@ -126,6 +128,8 @@ const normalize = (url, options, defaults) => {
} else {
url = url.replace(/^unix:/, 'http://$&');
url = urlParseLax(url);
url.searchParams = url.query;
delete url.query;
}
} else if (is(url) === 'URL') {
url = urlToOptions(url);
Expand All @@ -150,18 +154,30 @@ const normalize = (url, options, defaults) => {
get: () => baseUrl
});

const {query} = options;
if (is.nonEmptyString(query) || is.nonEmptyObject(query) || query instanceof URLSearchParams) {
if (!is.string(query)) {
if (!(query instanceof URLSearchParams)) {
validateSearchParams(query);
let searchParams;
if (options.query) {
if (!shownDeprecation) {
console.warn('`options.query` is deprecated. We support it solely for compatibility - it will be removed in Got 11. Use `options.searchParams` instead.');
shownDeprecation = true;
}

searchParams = options.query;
delete options.query;
} else if (options.searchParams) {
searchParams = options.searchParams;
delete options.searchParams;
}

if (is.nonEmptyString(searchParams) || is.nonEmptyObject(searchParams) || searchParams instanceof URLSearchParams) {
if (!is.string(searchParams)) {
if (!(searchParams instanceof URLSearchParams)) {
validateSearchParams(searchParams);
}

options.query = (new URLSearchParams(query)).toString();
searchParams = (new URLSearchParams(searchParams)).toString();
}

options.path = `${options.path.split('?')[0]}?${options.query}`;
delete options.query;
options.path = `${options.path.split('?')[0]}?${searchParams}`;
}

if (options.hostname === 'unix') {
Expand Down
28 changes: 14 additions & 14 deletions test/arguments.js
Expand Up @@ -54,7 +54,7 @@ test('url should be utf-8 encoded', async t => {
);
});

test('string url with query is preserved', async t => {
test('string url with searchParams is preserved', async t => {
const path = '/?test=http://example.com?foo=bar';
const {body} = await got(`${s.url}${path}`);
t.is(body, path);
Expand Down Expand Up @@ -90,11 +90,11 @@ test('requestUrl with url.parse object as first argument', async t => {
t.is((await got(parse(`${s.url}/test`))).requestUrl, `${s.url}/test`);
});

test('overrides querystring from options', async t => {
test('overrides searchParams from options', async t => {
const {body} = await got(
`${s.url}/?drop=this`,
{
query: {
searchParams: {
test: 'wow'
},
cache: {
Expand All @@ -111,24 +111,24 @@ test('overrides querystring from options', async t => {
t.is(body, '/?test=wow');
});

test('escapes query parameter values', async t => {
test('escapes searchParams parameter values', async t => {
const {body} = await got(`${s.url}`, {
query: {
searchParams: {
test: 'it’s ok'
}
});

t.is(body, '/?test=it%E2%80%99s+ok');
});

test('the `query` option can be a URLSearchParams', async t => {
const query = new URLSearchParams({test: 'wow'});
const {body} = await got(s.url, {query});
test('the `searchParams` option can be a URLSearchParams', async t => {
const searchParams = new URLSearchParams({test: 'wow'});
const {body} = await got(s.url, {searchParams});
t.is(body, '/?test=wow');
});

test('should ignore empty query object', async t => {
t.is((await got(`${s.url}/test`, {query: {}})).requestUrl, `${s.url}/test`);
test('should ignore empty searchParams object', async t => {
t.is((await got(`${s.url}/test`, {searchParams: {}})).requestUrl, `${s.url}/test`);
});

test('should throw when body is set to object', async t => {
Expand Down Expand Up @@ -224,17 +224,17 @@ test('throws when trying to modify baseUrl after options got normalized', async
await t.throwsAsync(instanceA('/'), 'Failed to set baseUrl. Options are normalized already.');
});

test('throws if the query key is invalid', async t => {
test('throws if the searchParams key is invalid', async t => {
await t.throwsAsync(() => got(s.url, {
query: {
searchParams: {
[[]]: []
}
}), TypeError);
});

test('throws if the query value is invalid', async t => {
test('throws if the searchParams value is invalid', async t => {
await t.throwsAsync(() => got(s.url, {
query: {
searchParams: {
foo: []
}
}), TypeError);
Expand Down
6 changes: 3 additions & 3 deletions test/http.js
Expand Up @@ -78,9 +78,9 @@ test('buffer on encoding === null', async t => {
t.true(is.buffer(data));
});

test('query option', async t => {
t.is((await got(s.url, {query: {recent: true}})).body, 'recent');
t.is((await got(s.url, {query: 'recent=true'})).body, 'recent');
test('searchParams option', async t => {
t.is((await got(s.url, {searchParams: {recent: true}})).body, 'recent');
t.is((await got(s.url, {searchParams: 'recent=true'})).body, 'recent');
});

test('requestUrl response when sending url as param', async t => {
Expand Down
96 changes: 96 additions & 0 deletions test/query.js
@@ -0,0 +1,96 @@
import {URLSearchParams} from 'url';
import test from 'ava';
import got from '../dist';
import {createServer} from './helpers/server';

// TODO: Remove this file before the Got v11 release together with completely removing the `query` option

let s;

test.before('setup', async () => {
s = await createServer();

const echoUrl = (request, response) => {
response.end(request.url);
};

s.on('/', (request, response) => {
response.statusCode = 404;
response.end();
});

s.on('/test', echoUrl);
s.on('/?test=wow', echoUrl);
s.on('/?test=it’s+ok', echoUrl);

s.on('/reached', (request, response) => {
response.end('reached');
});

s.on('/relativeQuery?bang', (request, response) => {
response.writeHead(302, {
location: '/reached'
});
response.end();
});

s.on('/?recent=true', (request, response) => {
response.end('recent');
});

await s.listen(s.port);
});

test.after('cleanup', async () => {
await s.close();
});

test('overrides query from options', async t => {
const {body} = await got(
`${s.url}/?drop=this`,
{
query: {
test: 'wow'
},
cache: {
get(key) {
t.is(key, `cacheable-request:GET:${s.url}/?test=wow`);
},
set(key) {
t.is(key, `cacheable-request:GET:${s.url}/?test=wow`);
}
}
}
);

t.is(body, '/?test=wow');
});

test('escapes query parameter values', async t => {
const {body} = await got(`${s.url}`, {
query: {
test: 'it’s ok'
}
});

t.is(body, '/?test=it%E2%80%99s+ok');
});

test('the `query` option can be a URLSearchParams', async t => {
const query = new URLSearchParams({test: 'wow'});
const {body} = await got(s.url, {query});
t.is(body, '/?test=wow');
});

test('should ignore empty query object', async t => {
t.is((await got(`${s.url}/test`, {query: {}})).requestUrl, `${s.url}/test`);
});

test('query option', async t => {
t.is((await got(s.url, {query: {recent: true}})).body, 'recent');
t.is((await got(s.url, {query: 'recent=true'})).body, 'recent');
});

test('query in options are not breaking redirects', async t => {
t.is((await got(`${s.url}/relativeQuery`, {query: 'bang'})).body, 'reached');
});
4 changes: 2 additions & 2 deletions test/redirects.js
Expand Up @@ -155,8 +155,8 @@ test('throws on endless redirect', async t => {
t.deepEqual(error.redirectUrls, new Array(10).fill(`${http.url}/endless`));
});

test('query in options are not breaking redirects', async t => {
t.is((await got(`${http.url}/relativeQuery`, {query: 'bang'})).body, 'reached');
test('searchParams in options are not breaking redirects', async t => {
t.is((await got(`${http.url}/relativeQuery`, {searchParams: 'bang'})).body, 'reached');
});

test('hostname+path in options are not breaking redirects', async t => {
Expand Down

0 comments on commit b223663

Please sign in to comment.