Skip to content

fix: Replace outdated HTTP request module #8282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
],
"license": "BSD-3-Clause",
"dependencies": {
"@graphql-yoga/node": "2.6.0",
"@graphql-tools/utils": "8.12.0",
"@graphql-tools/merge": "8.3.6",
"@graphql-tools/schema": "9.0.4",
"@graphql-tools/utils": "8.12.0",
"@graphql-yoga/node": "2.6.0",
"@parse/fs-files-adapter": "1.2.2",
"@parse/push-adapter": "4.1.2",
"axios": "1.1.3",
"bcryptjs": "2.4.3",
"body-parser": "1.20.1",
"commander": "5.1.0",
Expand All @@ -34,8 +35,8 @@
"follow-redirects": "1.15.2",
"graphql": "16.6.0",
"graphql-list-fields": "2.0.2",
"graphql-tag": "2.12.6",
"graphql-relay": "0.10.0",
"graphql-tag": "2.12.6",
"intersect": "1.0.1",
"jsonwebtoken": "8.5.1",
"jwks-rsa": "2.1.5",
Expand Down
22 changes: 10 additions & 12 deletions spec/HTTPRequest.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,17 @@ describe('httpRequest', () => {
});

it('should fail on 404', async () => {
await expectAsync(
httpRequest({
try {
await httpRequest({
url: `${httpRequestServer}/404`,
})
).toBeRejectedWith(
jasmine.objectContaining({
status: 404,
buffer: Buffer.from('NO'),
text: 'NO',
data: undefined,
})
);
});
fail('should have failed');
} catch (e) {
expect(e.status).toBe(404);
expect(e.buffer).toEqual(Buffer.from('NO'));
expect(e.text).toBe('NO');
expect(e.data).toBeUndefined();
}
});

it('should post on echo', async () => {
Expand Down Expand Up @@ -178,7 +177,6 @@ describe('httpRequest', () => {
url: `${httpRequestServer}/qs`,
params: 'foo=bar&foo2=bar2',
});

expect(httpResponse.status).toBe(200);
expect(httpResponse.data).toEqual({ foo: 'bar', foo2: 'bar2' });
});
Expand Down
12 changes: 7 additions & 5 deletions spec/PagesRouter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ describe('Pages Router', () => {
followRedirects: false,
}).catch(e => e);
expect(res.status).toBe(200);
expect(res.text).toEqual('"Password successfully reset"');
expect(res.text).toEqual('Password successfully reset');
});

it('request_password_reset: responds with AJAX error on missing password', async () => {
Expand Down Expand Up @@ -970,10 +970,12 @@ describe('Pages Router', () => {
// Do not compose this URL with `new URL(...)` because that would normalize
// the URL and remove path patterns; the path patterns must reach the router
const url = `${config.publicServerURL}/apps/../.gitignore`;
const response = await request({
url: url,
followRedirects: false,
}).catch(e => e);
const response = await request
.legacy({
url: url,
followRedirects: false,
})
.catch(e => e);
expect(response.status).toBe(404);
expect(response.text).toBe('Not found.');
});
Expand Down
2 changes: 1 addition & 1 deletion spec/ParseUser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2459,7 +2459,7 @@ describe('Parse.User testing', () => {
'X-Parse-REST-API-Key': 'rest',
},
url: 'http://localhost:8378/1/sessions/' + b.objectId,
body: JSON.stringify({ foo: 'bar' }),
body: { foo: 'bar' },
}).then(() => {
done();
});
Expand Down
6 changes: 3 additions & 3 deletions spec/RegexVulnerabilities.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ describe('Regex Vulnerabilities', function () {
await request({
url: `${serverURL}/apps/test/request_password_reset`,
method: 'POST',
body: {
body: JSON.stringify({
token: { $regex: '' },
username: '[email protected]',
new_password: 'newpassword',
},
}),
});
try {
await Parse.User.logIn('[email protected]', 'newpassword');
Expand Down Expand Up @@ -174,7 +174,7 @@ describe('Regex Vulnerabilities', function () {
expect(passwordResetResponse.headers.location).toMatch(
`\\/choose\\_password\\?token\\=${token}\\&`
);
await request({
await request.legacy({
url: `${serverURL}/apps/test/request_password_reset`,
method: 'POST',
body: {
Expand Down
32 changes: 17 additions & 15 deletions spec/ValidationAndPasswordsReset.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -690,20 +690,22 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
},
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
request({
url: 'http://localhost:8378/1/apps/test/resend_verification_email',
method: 'POST',
followRedirects: false,
body: {
username: 'sadfasga',
},
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/link_send_fail.html'
);
done();
});
request
.legacy({
url: 'http://localhost:8378/1/apps/test/resend_verification_email',
method: 'POST',
followRedirects: false,
body: {
username: 'sadfasga',
},
})
.then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/link_send_fail.html'
);
done();
});
});
});

Expand Down Expand Up @@ -975,7 +977,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
followRedirects: false,
});
expect(resetResponse.status).toEqual(200);
expect(resetResponse.text).toEqual('"Password successfully reset"');
expect(resetResponse.text).toEqual('Password successfully reset');

await Parse.User.logIn('zxcv', 'hello');
const config = Config.get('test');
Expand Down
10 changes: 2 additions & 8 deletions src/cloud-code/Parse.Cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -713,14 +713,8 @@ ParseCloud.useMasterKey = () => {
);
};

const request = require('./httpRequest');
ParseCloud.httpRequest = opts => {
Deprecator.logRuntimeDeprecation({
usage: 'Parse.Cloud.httpRequest',
solution: 'Use a http request library instead.',
});
return request(opts);
};
import axios from 'axios';
ParseCloud.httpRequest = axios;

module.exports = ParseCloud;

Expand Down
71 changes: 56 additions & 15 deletions src/cloud-code/httpRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,63 @@ const encodeBody = function ({ body, headers = {} }) {
*
* @method httpRequest
* @name Parse.Cloud.httpRequest
* @param {Parse.Cloud.HTTPOptions} options The Parse.Cloud.HTTPOptions object that makes the request.
* @return {Promise<Parse.Cloud.HTTPResponse>} A promise that will be resolved with a {@link Parse.Cloud.HTTPResponse} object when the request completes.
* @param {Object} options axios object for options
* @return {Promise<Object>} axios response object
*/
module.exports = function httpRequest(options) {
import axios from 'axios';
import { parse as qs } from 'querystring';
module.exports = async options => {
if (options.method) {
options.method = options.method.toLowerCase();
}
if (options.body) {
options.data = options.body;
delete options.body;
}
if (typeof options.params === 'object') {
options.qs = options.params;
} else if (typeof options.params === 'string') {
options.qs = qs(options.params);
}
if (options.qs) {
options.params = options.qs;
delete options.qs;
}
if (!options.followRedirects) {
options.maxRedirects = 0;
delete options.followRedirects;
}
try {
const response = await axios(options);
const data = response.data;
if (Object.prototype.toString.call(data) === '[object Object]') {
response.text = JSON.stringify(data);
response.data = data;
} else {
response.text = data;
}
response.buffer = Buffer.from(response.text);
return response;
} catch (e) {
e.status = e.response && e.response.status;
const data = e.response && e.response.data;
if (Object.prototype.toString.call(data) === '[object Object]') {
e.text = JSON.stringify(data);
e.data = data;
} else {
e.text = data;
}
e.buffer = Buffer.from(e.text);
if (e.response && e.response.headers) {
e.headers = e.response.headers;
}
if (e.status === 301 || e.status === 302 || e.status === 303) {
return e;
}
throw e;
}
};
module.exports.legacy = function httpRequest(options) {
let url;
try {
url = parse(options.url);
Expand Down Expand Up @@ -143,16 +196,4 @@ module.exports = function httpRequest(options) {
});
};

/**
* @typedef Parse.Cloud.HTTPOptions
* @property {String|Object} body The body of the request. If it is a JSON object, then the Content-Type set in the headers must be application/x-www-form-urlencoded or application/json. You can also set this to a {@link Buffer} object to send raw bytes. If you use a Buffer, you should also set the Content-Type header explicitly to describe what these bytes represent.
* @property {function} error The function that is called when the request fails. It will be passed a Parse.Cloud.HTTPResponse object.
* @property {Boolean} followRedirects Whether to follow redirects caused by HTTP 3xx responses. Defaults to false.
* @property {Object} headers The headers for the request.
* @property {String} method The method of the request. GET, POST, PUT, DELETE, HEAD, and OPTIONS are supported. Will default to GET if not specified.
* @property {String|Object} params The query portion of the url. You can pass a JSON object of key value pairs like params: {q : 'Sean Plott'} or a raw string like params:q=Sean Plott.
* @property {function} success The function that is called when the request successfully completes. It will be passed a Parse.Cloud.HTTPResponse object.
* @property {string} url The url to send the request to.
*/

module.exports.encodeBody = encodeBody;