Skip to content

Commit 1be83d0

Browse files
committed
feat: Allow custom server connection fail message
1 parent 6060792 commit 1be83d0

File tree

8 files changed

+117
-11
lines changed

8 files changed

+117
-11
lines changed

integration/test/ParseServerTest.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const assert = require('assert');
4+
const Parse = require('../../node');
45

56
describe('ParseServer', () => {
67
it('can reconfigure server', async () => {
@@ -20,4 +21,19 @@ describe('ParseServer', () => {
2021
await object.save();
2122
assert(object.id);
2223
});
24+
25+
it('can shutdown with custom message', async () => {
26+
const defaultMessage = Parse.CoreManager.get('CONNECTION_FAILED_MESSAGE');
27+
const message = 'Server is down!';
28+
Parse.CoreManager.set('CONNECTION_FAILED_MESSAGE', message);
29+
const parseServer = await reconfigureServer();
30+
const object = new TestObject({ foo: 'bar' });
31+
await parseServer.handleShutdown();
32+
await new Promise((resolve) => parseServer.server.close(resolve));
33+
await expectAsync(object.save()).toBeRejectedWithError(message);
34+
await reconfigureServer({});
35+
await object.save();
36+
assert(object.id);
37+
Parse.CoreManager.set('CONNECTION_FAILED_MESSAGE', defaultMessage);
38+
});
2339
});

src/CoreManager.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ const config: Config & { [key: string]: mixed } = {
177177
!!process.versions.node &&
178178
!process.versions.electron,
179179
REQUEST_ATTEMPT_LIMIT: 5,
180+
CONNECTION_FAILED_MESSAGE: 'XMLHttpRequest failed: "Unable to connect to the Parse API"',
180181
REQUEST_BATCH_SIZE: 20,
181182
REQUEST_HEADERS: {},
182183
SERVER_URL: 'https://api.parse.com/1',

src/EventuallyQueue.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ const EventuallyQueue = {
275275
await object.save(queueObject.object, queueObject.serverOptions);
276276
await this.remove(queueObject.queueId);
277277
} catch (e) {
278-
if (e.message !== 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {
278+
if (e.message !== CoreManager.get('CONNECTION_FAILED_MESSAGE')) {
279279
await this.remove(queueObject.queueId);
280280
}
281281
}
@@ -285,7 +285,7 @@ const EventuallyQueue = {
285285
await object.destroy(queueObject.serverOptions);
286286
await this.remove(queueObject.queueId);
287287
} catch (e) {
288-
if (e.message !== 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {
288+
if (e.message !== CoreManager.get('CONNECTION_FAILED_MESSAGE')) {
289289
await this.remove(queueObject.queueId);
290290
}
291291
}

src/ParseObject.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,7 @@ class ParseObject {
12191219
try {
12201220
await this.save(null, options);
12211221
} catch (e) {
1222-
if (e.message === 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {
1222+
if (e.message === CoreManager.get('CONNECTION_FAILED_MESSAGE')) {
12231223
await EventuallyQueue.save(this, options);
12241224
EventuallyQueue.poll();
12251225
}
@@ -1363,7 +1363,7 @@ class ParseObject {
13631363
try {
13641364
await this.destroy(options);
13651365
} catch (e) {
1366-
if (e.message === 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {
1366+
if (e.message === CoreManager.get('CONNECTION_FAILED_MESSAGE')) {
13671367
await EventuallyQueue.destroy(this, options);
13681368
EventuallyQueue.poll();
13691369
}

src/RESTController.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ const RESTController = {
132132
const delay = Math.round(Math.random() * 125 * Math.pow(2, attempts));
133133
setTimeout(dispatch, delay);
134134
} else if (xhr.status === 0) {
135-
promise.reject('Unable to connect to the Parse API');
135+
promise.reject(new ParseError(ParseError.CONNECTION_FAILED, CoreManager.get('CONNECTION_FAILED_MESSAGE')));
136136
} else {
137137
// After the retry limit is reached, fail
138138
promise.reject(xhr);
@@ -316,10 +316,7 @@ const RESTController = {
316316
}
317317
} else {
318318
const message = response.message ? response.message : response;
319-
error = new ParseError(
320-
ParseError.CONNECTION_FAILED,
321-
'XMLHttpRequest failed: ' + JSON.stringify(message)
322-
);
319+
error = new ParseError(ParseError.CONNECTION_FAILED, message);
323320
}
324321
return Promise.reject(error);
325322
},

src/__tests__/EventuallyQueue-test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,27 @@ describe('EventuallyQueue', () => {
256256
expect(EventuallyQueue.remove).toHaveBeenCalledTimes(0);
257257
});
258258

259+
it('should not remove if queue destroy callback network fails with custom connection message', async () => {
260+
const defaultMessage = CoreManager.get('CONNECTION_FAILED_MESSAGE');
261+
const message = 'Server is down!';
262+
CoreManager.set('CONNECTION_FAILED_MESSAGE', message);
263+
const object = new ParseObject('TestObject');
264+
jest.spyOn(object, 'destroy').mockImplementationOnce(() => {
265+
throw new ParseError(ParseError.CONNECTION_FAILED, message);
266+
});
267+
jest.spyOn(EventuallyQueue, 'remove').mockImplementationOnce(() => {});
268+
const queueObject = {
269+
action: 'destroy',
270+
queueId: 'queue1',
271+
serverOptions: {},
272+
};
273+
await EventuallyQueue.sendQueueCallback(object, queueObject);
274+
expect(object.destroy).toHaveBeenCalledTimes(1);
275+
expect(object.destroy).toHaveBeenCalledWith({});
276+
expect(EventuallyQueue.remove).toHaveBeenCalledTimes(0);
277+
CoreManager.set('CONNECTION_FAILED_MESSAGE', defaultMessage);
278+
});
279+
259280
it('can handle send queue save callback with no object', async () => {
260281
jest.spyOn(EventuallyQueue, 'remove').mockImplementationOnce(() => {});
261282
const object = null;
@@ -327,6 +348,28 @@ describe('EventuallyQueue', () => {
327348
expect(EventuallyQueue.remove).toHaveBeenCalledTimes(0);
328349
});
329350

351+
it('should not remove if queue save callback network fails with custom connection message', async () => {
352+
const defaultMessage = CoreManager.get('CONNECTION_FAILED_MESSAGE');
353+
const message = 'Server is down!';
354+
CoreManager.set('CONNECTION_FAILED_MESSAGE', message);
355+
const object = new ParseObject('TestObject');
356+
jest.spyOn(object, 'save').mockImplementationOnce(() => {
357+
throw new ParseError(ParseError.CONNECTION_FAILED, message);
358+
});
359+
jest.spyOn(EventuallyQueue, 'remove').mockImplementationOnce(() => {});
360+
const queueObject = {
361+
action: 'save',
362+
queueId: 'queue2',
363+
object: { foo: 'bar' },
364+
serverOptions: {},
365+
};
366+
await EventuallyQueue.sendQueueCallback(object, queueObject);
367+
expect(object.save).toHaveBeenCalledTimes(1);
368+
expect(object.save).toHaveBeenCalledWith({ foo: 'bar' }, {});
369+
expect(EventuallyQueue.remove).toHaveBeenCalledTimes(0);
370+
CoreManager.set('CONNECTION_FAILED_MESSAGE', defaultMessage);
371+
});
372+
330373
it('can handle send queue save callback if queue is old', async () => {
331374
jest.spyOn(EventuallyQueue, 'remove').mockImplementationOnce(() => {});
332375
const today = new Date();

src/__tests__/ParseObject-test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,22 @@ describe('ParseObject', () => {
15851585
expect(EventuallyQueue.poll).toHaveBeenCalledTimes(1);
15861586
});
15871587

1588+
it('can save the object eventually on network failure with custom connection message', async () => {
1589+
const defaultMessage = CoreManager.get('CONNECTION_FAILED_MESSAGE');
1590+
const message = 'Server is down!';
1591+
CoreManager.set('CONNECTION_FAILED_MESSAGE', message);
1592+
const p = new ParseObject('Person');
1593+
jest.spyOn(EventuallyQueue, 'save').mockImplementationOnce(() => Promise.resolve());
1594+
jest.spyOn(EventuallyQueue, 'poll').mockImplementationOnce(() => {});
1595+
jest.spyOn(p, 'save').mockImplementationOnce(() => {
1596+
throw new ParseError(ParseError.CONNECTION_FAILED, message);
1597+
});
1598+
await p.saveEventually();
1599+
expect(EventuallyQueue.save).toHaveBeenCalledTimes(1);
1600+
expect(EventuallyQueue.poll).toHaveBeenCalledTimes(1);
1601+
CoreManager.set('CONNECTION_FAILED_MESSAGE', defaultMessage);
1602+
});
1603+
15881604
it('should not save the object eventually on error', async () => {
15891605
const p = new ParseObject('Person');
15901606
jest.spyOn(EventuallyQueue, 'save').mockImplementationOnce(() => Promise.resolve());
@@ -2924,6 +2940,22 @@ describe('ObjectController', () => {
29242940
expect(EventuallyQueue.poll).toHaveBeenCalledTimes(1);
29252941
});
29262942

2943+
it('can destroy the object eventually on network failure with custom connection message', async () => {
2944+
const defaultMessage = CoreManager.get('CONNECTION_FAILED_MESSAGE');
2945+
const message = 'Server is down!';
2946+
CoreManager.set('CONNECTION_FAILED_MESSAGE', message);
2947+
const p = new ParseObject('Person');
2948+
jest.spyOn(EventuallyQueue, 'destroy').mockImplementationOnce(() => Promise.resolve());
2949+
jest.spyOn(EventuallyQueue, 'poll').mockImplementationOnce(() => {});
2950+
jest.spyOn(p, 'destroy').mockImplementationOnce(() => {
2951+
throw new ParseError(ParseError.CONNECTION_FAILED, message);
2952+
});
2953+
await p.destroyEventually();
2954+
expect(EventuallyQueue.destroy).toHaveBeenCalledTimes(1);
2955+
expect(EventuallyQueue.poll).toHaveBeenCalledTimes(1);
2956+
CoreManager.set('CONNECTION_FAILED_MESSAGE', defaultMessage);
2957+
});
2958+
29272959
it('should not destroy object eventually on error', async () => {
29282960
const p = new ParseObject('Person');
29292961
jest.spyOn(EventuallyQueue, 'destroy').mockImplementationOnce(() => Promise.resolve());

src/__tests__/RESTController-test.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,29 @@ describe('RESTController', () => {
7272
mockXHR([{ status: 0 }, { status: 0 }, { status: 0 }, { status: 0 }, { status: 0 }])
7373
);
7474
RESTController.ajax('POST', 'users', {}).then(null, err => {
75-
expect(err).toBe('Unable to connect to the Parse API');
75+
expect(err.code).toBe(100);
76+
expect(err.message).toBe('XMLHttpRequest failed: "Unable to connect to the Parse API"');
7677
done();
7778
});
7879
jest.runAllTimers();
7980
});
8081

82+
it('retries on connection failure custom message', done => {
83+
const defaultMessage = CoreManager.get('CONNECTION_FAILED_MESSAGE');
84+
const message = 'Server is down!';
85+
CoreManager.set('CONNECTION_FAILED_MESSAGE', message);
86+
RESTController._setXHR(
87+
mockXHR([{ status: 0 }, { status: 0 }, { status: 0 }, { status: 0 }, { status: 0 }])
88+
);
89+
RESTController.ajax('POST', 'users', {}).then(null, err => {
90+
expect(err.code).toBe(100);
91+
expect(err.message).toBe(message);
92+
done();
93+
});
94+
jest.runAllTimers();
95+
CoreManager.set('CONNECTION_FAILED_MESSAGE', defaultMessage);
96+
});
97+
8198
it('returns a connection error on network failure', async () => {
8299
expect.assertions(2);
83100
RESTController._setXHR(
@@ -182,7 +199,7 @@ describe('RESTController', () => {
182199
RESTController._setXHR(XHR);
183200
RESTController.request('GET', 'classes/MyObject', {}, {}).then(null, error => {
184201
expect(error.code).toBe(100);
185-
expect(error.message.indexOf('XMLHttpRequest failed')).toBe(0);
202+
expect(error.message.indexOf('SyntaxError')).toBe(0);
186203
done();
187204
});
188205
});

0 commit comments

Comments
 (0)