fix: Parse Server doesn't shutdown gracefully (#9634)
This commit is contained in:
@@ -3,11 +3,9 @@ const request = require('../lib/request');
|
|||||||
describe('Enable express error handler', () => {
|
describe('Enable express error handler', () => {
|
||||||
it('should call the default handler in case of error, like updating a non existing object', async done => {
|
it('should call the default handler in case of error, like updating a non existing object', async done => {
|
||||||
spyOn(console, 'error');
|
spyOn(console, 'error');
|
||||||
const parseServer = await reconfigureServer(
|
const parseServer = await reconfigureServer({
|
||||||
Object.assign({}, defaultConfiguration, {
|
enableExpressErrorHandler: true,
|
||||||
enableExpressErrorHandler: true,
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
parseServer.app.use(function (err, req, res, next) {
|
parseServer.app.use(function (err, req, res, next) {
|
||||||
expect(err.message).toBe('Object not found.');
|
expect(err.message).toBe('Object not found.');
|
||||||
next(err);
|
next(err);
|
||||||
|
|||||||
@@ -33,9 +33,7 @@ describe('miscellaneous', () => {
|
|||||||
expect(results.length).toEqual(1);
|
expect(results.length).toEqual(1);
|
||||||
expect(results[0]['foo']).toEqual('bar');
|
expect(results[0]['foo']).toEqual('bar');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('miscellaneous', function () {
|
|
||||||
it('create a GameScore object', function (done) {
|
it('create a GameScore object', function (done) {
|
||||||
const obj = new Parse.Object('GameScore');
|
const obj = new Parse.Object('GameScore');
|
||||||
obj.set('score', 1337);
|
obj.set('score', 1337);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ describe('Config Keys', () => {
|
|||||||
|
|
||||||
it('recognizes invalid keys in root', async () => {
|
it('recognizes invalid keys in root', async () => {
|
||||||
await expectAsync(reconfigureServer({
|
await expectAsync(reconfigureServer({
|
||||||
...defaultConfiguration,
|
|
||||||
invalidKey: 1,
|
invalidKey: 1,
|
||||||
})).toBeResolved();
|
})).toBeResolved();
|
||||||
const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '');
|
const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '');
|
||||||
@@ -21,7 +20,6 @@ describe('Config Keys', () => {
|
|||||||
|
|
||||||
it('recognizes invalid keys in pages.customUrls', async () => {
|
it('recognizes invalid keys in pages.customUrls', async () => {
|
||||||
await expectAsync(reconfigureServer({
|
await expectAsync(reconfigureServer({
|
||||||
...defaultConfiguration,
|
|
||||||
pages: {
|
pages: {
|
||||||
customUrls: {
|
customUrls: {
|
||||||
invalidKey: 1,
|
invalidKey: 1,
|
||||||
@@ -37,7 +35,6 @@ describe('Config Keys', () => {
|
|||||||
|
|
||||||
it('recognizes invalid keys in liveQueryServerOptions', async () => {
|
it('recognizes invalid keys in liveQueryServerOptions', async () => {
|
||||||
await expectAsync(reconfigureServer({
|
await expectAsync(reconfigureServer({
|
||||||
...defaultConfiguration,
|
|
||||||
liveQueryServerOptions: {
|
liveQueryServerOptions: {
|
||||||
invalidKey: 1,
|
invalidKey: 1,
|
||||||
MasterKey: 1,
|
MasterKey: 1,
|
||||||
@@ -50,7 +47,6 @@ describe('Config Keys', () => {
|
|||||||
|
|
||||||
it('recognizes invalid keys in rateLimit', async () => {
|
it('recognizes invalid keys in rateLimit', async () => {
|
||||||
await expectAsync(reconfigureServer({
|
await expectAsync(reconfigureServer({
|
||||||
...defaultConfiguration,
|
|
||||||
rateLimit: [
|
rateLimit: [
|
||||||
{ invalidKey: 1 },
|
{ invalidKey: 1 },
|
||||||
{ RequestPath: 1 },
|
{ RequestPath: 1 },
|
||||||
@@ -64,7 +60,7 @@ describe('Config Keys', () => {
|
|||||||
expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow');
|
expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('recognizes valid keys in default configuration', async () => {
|
it_only_db('mongo')('recognizes valid keys in default configuration', async () => {
|
||||||
await expectAsync(reconfigureServer({
|
await expectAsync(reconfigureServer({
|
||||||
...defaultConfiguration,
|
...defaultConfiguration,
|
||||||
})).toBeResolved();
|
})).toBeResolved();
|
||||||
|
|||||||
@@ -431,17 +431,32 @@ describe('ParseGraphQLServer', () => {
|
|||||||
objects.push(object1, object2, object3, object4);
|
objects.push(object1, object2, object3, object4);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
async function createGQLFromParseServer(_parseServer) {
|
||||||
|
if (parseLiveQueryServer) {
|
||||||
|
await parseLiveQueryServer.server.close();
|
||||||
|
}
|
||||||
|
if (httpServer) {
|
||||||
|
await httpServer.close();
|
||||||
|
}
|
||||||
const expressApp = express();
|
const expressApp = express();
|
||||||
httpServer = http.createServer(expressApp);
|
httpServer = http.createServer(expressApp);
|
||||||
expressApp.use('/parse', parseServer.app);
|
expressApp.use('/parse', _parseServer.app);
|
||||||
parseLiveQueryServer = await ParseServer.createLiveQueryServer(httpServer, {
|
parseLiveQueryServer = await ParseServer.createLiveQueryServer(httpServer, {
|
||||||
port: 1338,
|
port: 1338,
|
||||||
});
|
});
|
||||||
|
parseGraphQLServer = new ParseGraphQLServer(_parseServer, {
|
||||||
|
graphQLPath: '/graphql',
|
||||||
|
playgroundPath: '/playground',
|
||||||
|
subscriptionsPath: '/subscriptions',
|
||||||
|
});
|
||||||
parseGraphQLServer.applyGraphQL(expressApp);
|
parseGraphQLServer.applyGraphQL(expressApp);
|
||||||
parseGraphQLServer.applyPlayground(expressApp);
|
parseGraphQLServer.applyPlayground(expressApp);
|
||||||
parseGraphQLServer.createSubscriptions(httpServer);
|
parseGraphQLServer.createSubscriptions(httpServer);
|
||||||
await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve));
|
await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve));
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
|
|
||||||
const subscriptionClient = new SubscriptionClient(
|
const subscriptionClient = new SubscriptionClient(
|
||||||
'ws://localhost:13377/subscriptions',
|
'ws://localhost:13377/subscriptions',
|
||||||
@@ -753,10 +768,6 @@ describe('ParseGraphQLServer', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await resetGraphQLCache();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have Node interface', async () => {
|
it('should have Node interface', async () => {
|
||||||
const schemaTypes = (
|
const schemaTypes = (
|
||||||
await apolloClient.query({
|
await apolloClient.query({
|
||||||
@@ -2821,7 +2832,8 @@ describe('ParseGraphQLServer', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('Id inputs should work either with global id or object id with objectId higher than 19', async () => {
|
it('Id inputs should work either with global id or object id with objectId higher than 19', async () => {
|
||||||
await reconfigureServer({ objectIdSize: 20 });
|
const parseServer = await reconfigureServer({ objectIdSize: 20 });
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const obj = new Parse.Object('SomeClass');
|
const obj = new Parse.Object('SomeClass');
|
||||||
await obj.save({ name: 'aname', type: 'robot' });
|
await obj.save({ name: 'aname', type: 'robot' });
|
||||||
const result = await apolloClient.query({
|
const result = await apolloClient.query({
|
||||||
@@ -5328,7 +5340,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
parseServer = await global.reconfigureServer({
|
parseServer = await global.reconfigureServer({
|
||||||
maxLimit: 10,
|
maxLimit: 10,
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const promises = [];
|
const promises = [];
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
const obj = new Parse.Object('SomeClass');
|
const obj = new Parse.Object('SomeClass');
|
||||||
@@ -6841,7 +6853,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
parseServer = await global.reconfigureServer({
|
parseServer = await global.reconfigureServer({
|
||||||
publicServerURL: 'http://localhost:13377/parse',
|
publicServerURL: 'http://localhost:13377/parse',
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
body.append(
|
body.append(
|
||||||
'operations',
|
'operations',
|
||||||
@@ -7049,6 +7061,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
challengeAdapter,
|
challengeAdapter,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const clientMutationId = uuidv4();
|
const clientMutationId = uuidv4();
|
||||||
|
|
||||||
const result = await apolloClient.mutate({
|
const result = await apolloClient.mutate({
|
||||||
@@ -7095,6 +7108,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
challengeAdapter,
|
challengeAdapter,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const clientMutationId = uuidv4();
|
const clientMutationId = uuidv4();
|
||||||
const userSchema = new Parse.Schema('_User');
|
const userSchema = new Parse.Schema('_User');
|
||||||
userSchema.addString('someField');
|
userSchema.addString('someField');
|
||||||
@@ -7169,7 +7183,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
userSchema.addString('someField');
|
userSchema.addString('someField');
|
||||||
userSchema.addPointer('aPointer', '_User');
|
userSchema.addPointer('aPointer', '_User');
|
||||||
await userSchema.update();
|
await userSchema.update();
|
||||||
@@ -7239,7 +7253,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
challengeAdapter,
|
challengeAdapter,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const user = new Parse.User();
|
const user = new Parse.User();
|
||||||
await user.save({ username: 'username', password: 'password' });
|
await user.save({ username: 'username', password: 'password' });
|
||||||
|
|
||||||
@@ -7310,6 +7324,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
challengeAdapter,
|
challengeAdapter,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const clientMutationId = uuidv4();
|
const clientMutationId = uuidv4();
|
||||||
const user = new Parse.User();
|
const user = new Parse.User();
|
||||||
user.setUsername('user1');
|
user.setUsername('user1');
|
||||||
@@ -7441,6 +7456,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
emailAdapter: emailAdapter,
|
emailAdapter: emailAdapter,
|
||||||
publicServerURL: 'http://test.test',
|
publicServerURL: 'http://test.test',
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const user = new Parse.User();
|
const user = new Parse.User();
|
||||||
user.setUsername('user1');
|
user.setUsername('user1');
|
||||||
user.setPassword('user1');
|
user.setPassword('user1');
|
||||||
@@ -7488,6 +7504,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const user = new Parse.User();
|
const user = new Parse.User();
|
||||||
user.setUsername('user1');
|
user.setUsername('user1');
|
||||||
user.setPassword('user1');
|
user.setPassword('user1');
|
||||||
@@ -7550,6 +7567,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
emailAdapter: emailAdapter,
|
emailAdapter: emailAdapter,
|
||||||
publicServerURL: 'http://test.test',
|
publicServerURL: 'http://test.test',
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const user = new Parse.User();
|
const user = new Parse.User();
|
||||||
user.setUsername('user1');
|
user.setUsername('user1');
|
||||||
user.setPassword('user1');
|
user.setPassword('user1');
|
||||||
@@ -9306,7 +9324,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
parseServer = await global.reconfigureServer({
|
parseServer = await global.reconfigureServer({
|
||||||
publicServerURL: 'http://localhost:13377/parse',
|
publicServerURL: 'http://localhost:13377/parse',
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
body.append(
|
body.append(
|
||||||
'operations',
|
'operations',
|
||||||
@@ -9339,7 +9357,6 @@ describe('ParseGraphQLServer', () => {
|
|||||||
headers,
|
headers,
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res.status).toEqual(200);
|
expect(res.status).toEqual(200);
|
||||||
|
|
||||||
const result = JSON.parse(await res.text());
|
const result = JSON.parse(await res.text());
|
||||||
@@ -9553,6 +9570,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
parseServer = await global.reconfigureServer({
|
parseServer = await global.reconfigureServer({
|
||||||
publicServerURL: 'http://localhost:13377/parse',
|
publicServerURL: 'http://localhost:13377/parse',
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||||
await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', {
|
await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', {
|
||||||
someField: { type: 'File', required: true },
|
someField: { type: 'File', required: true },
|
||||||
@@ -9617,6 +9635,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
parseServer = await global.reconfigureServer({
|
parseServer = await global.reconfigureServer({
|
||||||
publicServerURL: 'http://localhost:13377/parse',
|
publicServerURL: 'http://localhost:13377/parse',
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const schema = new Parse.Schema('SomeClass');
|
const schema = new Parse.Schema('SomeClass');
|
||||||
schema.addFile('someFileField');
|
schema.addFile('someFileField');
|
||||||
schema.addPointer('somePointerField', 'SomeClass');
|
schema.addPointer('somePointerField', 'SomeClass');
|
||||||
@@ -9725,7 +9744,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
parseServer = await global.reconfigureServer({
|
parseServer = await global.reconfigureServer({
|
||||||
publicServerURL: 'http://localhost:13377/parse',
|
publicServerURL: 'http://localhost:13377/parse',
|
||||||
});
|
});
|
||||||
|
await createGQLFromParseServer(parseServer);
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
body.append(
|
body.append(
|
||||||
'operations',
|
'operations',
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
const http = require('http');
|
||||||
const Auth = require('../lib/Auth');
|
const Auth = require('../lib/Auth');
|
||||||
const UserController = require('../lib/Controllers/UserController').UserController;
|
const UserController = require('../lib/Controllers/UserController').UserController;
|
||||||
const Config = require('../lib/Config');
|
const Config = require('../lib/Config');
|
||||||
const ParseServer = require('../lib/index').ParseServer;
|
const ParseServer = require('../lib/index').ParseServer;
|
||||||
const triggers = require('../lib/triggers');
|
const triggers = require('../lib/triggers');
|
||||||
const { resolvingPromise, sleep } = require('../lib/TestUtils');
|
const { resolvingPromise, sleep, getConnectionsCount } = require('../lib/TestUtils');
|
||||||
|
const request = require('../lib/request');
|
||||||
const validatorFail = () => {
|
const validatorFail = () => {
|
||||||
throw 'you are not authorized';
|
throw 'you are not authorized';
|
||||||
};
|
};
|
||||||
@@ -1181,6 +1183,78 @@ describe('ParseLiveQuery', function () {
|
|||||||
await new Promise(resolve => server.server.close(resolve));
|
await new Promise(resolve => server.server.close(resolve));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it_id('45655b74-716f-4fa1-a058-67eb21f3c3db')(it)('does shutdown separate liveQuery server', async () => {
|
||||||
|
await reconfigureServer({ appId: 'test_app_id' });
|
||||||
|
let close = false;
|
||||||
|
const config = {
|
||||||
|
appId: 'hello_test',
|
||||||
|
masterKey: 'world',
|
||||||
|
port: 1345,
|
||||||
|
mountPath: '/1',
|
||||||
|
serverURL: 'http://localhost:1345/1',
|
||||||
|
liveQuery: {
|
||||||
|
classNames: ['Yolo'],
|
||||||
|
},
|
||||||
|
startLiveQueryServer: true,
|
||||||
|
verbose: false,
|
||||||
|
silent: true,
|
||||||
|
liveQueryServerOptions: {
|
||||||
|
port: 1346,
|
||||||
|
},
|
||||||
|
serverCloseComplete: () => {
|
||||||
|
close = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
|
||||||
|
config.databaseAdapter = new databaseAdapter.constructor({
|
||||||
|
uri: databaseURI,
|
||||||
|
collectionPrefix: 'test_',
|
||||||
|
});
|
||||||
|
config.filesAdapter = defaultConfiguration.filesAdapter;
|
||||||
|
}
|
||||||
|
const parseServer = await ParseServer.startApp(config);
|
||||||
|
expect(parseServer.liveQueryServer).toBeDefined();
|
||||||
|
expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server);
|
||||||
|
|
||||||
|
// Open a connection to the liveQuery server
|
||||||
|
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
|
||||||
|
client.serverURL = 'ws://localhost:1346/1';
|
||||||
|
const query = await new Parse.Query('Yolo').subscribe();
|
||||||
|
|
||||||
|
// Open a connection to the parse server
|
||||||
|
const health = await request({
|
||||||
|
method: 'GET',
|
||||||
|
url: `http://localhost:1345/1/health`,
|
||||||
|
json: true,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': 'hello_test',
|
||||||
|
'X-Parse-Master-Key': 'world',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
agent: new http.Agent({ keepAlive: true }),
|
||||||
|
}).then(res => res.data);
|
||||||
|
expect(health.status).toBe('ok');
|
||||||
|
|
||||||
|
let parseConnectionCount = await getConnectionsCount(parseServer.server);
|
||||||
|
let liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server);
|
||||||
|
|
||||||
|
expect(parseConnectionCount > 0).toBe(true);
|
||||||
|
expect(liveQueryConnectionCount > 0).toBe(true);
|
||||||
|
await Promise.all([
|
||||||
|
parseServer.handleShutdown(),
|
||||||
|
new Promise(resolve => query.on('close', resolve)),
|
||||||
|
]);
|
||||||
|
expect(close).toBe(true);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
expect(parseServer.liveQueryServer.server.address()).toBeNull();
|
||||||
|
expect(parseServer.liveQueryServer.subscriber.isOpen).toBeFalse();
|
||||||
|
|
||||||
|
parseConnectionCount = await getConnectionsCount(parseServer.server);
|
||||||
|
liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server);
|
||||||
|
expect(parseConnectionCount).toBe(0);
|
||||||
|
expect(liveQueryConnectionCount).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('prevent afterSave trigger if not exists', async () => {
|
it('prevent afterSave trigger if not exists', async () => {
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
liveQuery: {
|
liveQuery: {
|
||||||
|
|||||||
@@ -3,11 +3,8 @@
|
|||||||
const Config = require('../lib/Config');
|
const Config = require('../lib/Config');
|
||||||
const Parse = require('parse/node');
|
const Parse = require('parse/node');
|
||||||
const request = require('../lib/request');
|
const request = require('../lib/request');
|
||||||
let databaseAdapter;
|
|
||||||
|
|
||||||
const fullTextHelper = async () => {
|
const fullTextHelper = async () => {
|
||||||
const config = Config.get('test');
|
|
||||||
databaseAdapter = config.database.adapter;
|
|
||||||
const subjects = [
|
const subjects = [
|
||||||
'coffee',
|
'coffee',
|
||||||
'Coffee Shopping',
|
'Coffee Shopping',
|
||||||
@@ -18,12 +15,6 @@ const fullTextHelper = async () => {
|
|||||||
'coffee and cream',
|
'coffee and cream',
|
||||||
'Cafe con Leche',
|
'Cafe con Leche',
|
||||||
];
|
];
|
||||||
await reconfigureServer({
|
|
||||||
appId: 'test',
|
|
||||||
restAPIKey: 'test',
|
|
||||||
publicServerURL: 'http://localhost:8378/1',
|
|
||||||
databaseAdapter,
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(
|
await Parse.Object.saveAll(
|
||||||
subjects.map(subject => new Parse.Object('TestObject').set({ subject, comment: subject }))
|
subjects.map(subject => new Parse.Object('TestObject').set({ subject, comment: subject }))
|
||||||
);
|
);
|
||||||
@@ -101,7 +92,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
|||||||
body: { where, _method: 'GET' },
|
body: { where, _method: 'GET' },
|
||||||
headers: {
|
headers: {
|
||||||
'X-Parse-Application-Id': 'test',
|
'X-Parse-Application-Id': 'test',
|
||||||
'X-Parse-REST-API-Key': 'test',
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -189,7 +180,7 @@ describe_only_db('mongo')('[mongodb] Parse.Query Full Text Search testing', () =
|
|||||||
url: 'http://localhost:8378/1/schemas/TestObject',
|
url: 'http://localhost:8378/1/schemas/TestObject',
|
||||||
headers: {
|
headers: {
|
||||||
'X-Parse-Application-Id': 'test',
|
'X-Parse-Application-Id': 'test',
|
||||||
'X-Parse-REST-API-Key': 'test',
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
'X-Parse-Master-Key': 'test',
|
'X-Parse-Master-Key': 'test',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
@@ -220,7 +211,7 @@ describe_only_db('mongo')('[mongodb] Parse.Query Full Text Search testing', () =
|
|||||||
body: { where, _method: 'GET' },
|
body: { where, _method: 'GET' },
|
||||||
headers: {
|
headers: {
|
||||||
'X-Parse-Application-Id': 'test',
|
'X-Parse-Application-Id': 'test',
|
||||||
'X-Parse-REST-API-Key': 'test',
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -288,7 +279,7 @@ describe_only_db('postgres')('[postgres] Parse.Query Full Text Search testing',
|
|||||||
body: { where, _method: 'GET' },
|
body: { where, _method: 'GET' },
|
||||||
headers: {
|
headers: {
|
||||||
'X-Parse-Application-Id': 'test',
|
'X-Parse-Application-Id': 'test',
|
||||||
'X-Parse-REST-API-Key': 'test',
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -322,7 +313,7 @@ describe_only_db('postgres')('[postgres] Parse.Query Full Text Search testing',
|
|||||||
body: { where, _method: 'GET' },
|
body: { where, _method: 'GET' },
|
||||||
headers: {
|
headers: {
|
||||||
'X-Parse-Application-Id': 'test',
|
'X-Parse-Application-Id': 'test',
|
||||||
'X-Parse-REST-API-Key': 'test',
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
/* Tests for ParseServer.js */
|
/* Tests for ParseServer.js */
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default;
|
|
||||||
const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter')
|
|
||||||
.default;
|
|
||||||
const ParseServer = require('../lib/ParseServer').default;
|
const ParseServer = require('../lib/ParseServer').default;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { spawn } = require('child_process');
|
const { spawn } = require('child_process');
|
||||||
@@ -45,49 +42,6 @@ describe('Server Url Checks', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('handleShutdown, close connection', done => {
|
|
||||||
const mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
|
|
||||||
const postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
|
|
||||||
let databaseAdapter;
|
|
||||||
if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
|
|
||||||
databaseAdapter = new PostgresStorageAdapter({
|
|
||||||
uri: process.env.PARSE_SERVER_TEST_DATABASE_URI || postgresURI,
|
|
||||||
collectionPrefix: 'test_',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
databaseAdapter = new MongoStorageAdapter({
|
|
||||||
uri: mongoURI,
|
|
||||||
collectionPrefix: 'test_',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let close = false;
|
|
||||||
const newConfiguration = Object.assign({}, defaultConfiguration, {
|
|
||||||
databaseAdapter,
|
|
||||||
serverStartComplete: () => {
|
|
||||||
let promise = Promise.resolve();
|
|
||||||
if (process.env.PARSE_SERVER_TEST_DB !== 'postgres') {
|
|
||||||
promise = parseServer.config.filesController.adapter._connect();
|
|
||||||
}
|
|
||||||
promise.then(() => {
|
|
||||||
parseServer.handleShutdown();
|
|
||||||
parseServer.server.close(err => {
|
|
||||||
if (err) {
|
|
||||||
done.fail('Close Server Error');
|
|
||||||
}
|
|
||||||
reconfigureServer({}).then(() => {
|
|
||||||
expect(close).toBe(true);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
serverCloseComplete: () => {
|
|
||||||
close = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const parseServer = ParseServer.startApp(newConfiguration);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not have unhandled promise rejection in the case of load error', done => {
|
it('does not have unhandled promise rejection in the case of load error', done => {
|
||||||
const parseServerProcess = spawn(path.resolve(__dirname, './support/FailingServer.js'));
|
const parseServerProcess = spawn(path.resolve(__dirname, './support/FailingServer.js'));
|
||||||
let stdout;
|
let stdout;
|
||||||
|
|||||||
@@ -282,7 +282,6 @@ describe('ParseServerRESTController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should generate separate session for each call', async () => {
|
it('should generate separate session for each call', async () => {
|
||||||
await reconfigureServer();
|
|
||||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||||
await myObject.save({ key: 'stringField' });
|
await myObject.save({ key: 'stringField' });
|
||||||
await myObject.destroy();
|
await myObject.destroy();
|
||||||
|
|||||||
@@ -182,6 +182,9 @@ describe('PushController', () => {
|
|||||||
return ['ios', 'android'];
|
return ['ios', 'android'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const payload = {
|
const payload = {
|
||||||
data: {
|
data: {
|
||||||
alert: 'Hello World!',
|
alert: 'Hello World!',
|
||||||
@@ -212,9 +215,6 @@ describe('PushController', () => {
|
|||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
};
|
};
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(installations);
|
await Parse.Object.saveAll(installations);
|
||||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||||
await pushCompleted(pushStatusId);
|
await pushCompleted(pushStatusId);
|
||||||
@@ -247,6 +247,9 @@ describe('PushController', () => {
|
|||||||
return ['ios', 'android'];
|
return ['ios', 'android'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const payload = {
|
const payload = {
|
||||||
data: {
|
data: {
|
||||||
alert: 'Hello World!',
|
alert: 'Hello World!',
|
||||||
@@ -277,9 +280,6 @@ describe('PushController', () => {
|
|||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
};
|
};
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(installations);
|
await Parse.Object.saveAll(installations);
|
||||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||||
await pushCompleted(pushStatusId);
|
await pushCompleted(pushStatusId);
|
||||||
@@ -309,7 +309,9 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const payload = {
|
const payload = {
|
||||||
data: {
|
data: {
|
||||||
alert: 'Hello World!',
|
alert: 'Hello World!',
|
||||||
@@ -331,9 +333,6 @@ describe('PushController', () => {
|
|||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
};
|
};
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(installations);
|
await Parse.Object.saveAll(installations);
|
||||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||||
await pushCompleted(pushStatusId);
|
await pushCompleted(pushStatusId);
|
||||||
@@ -382,14 +381,13 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
};
|
};
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(installations);
|
await Parse.Object.saveAll(installations);
|
||||||
const objectIds = installations.map(installation => {
|
const objectIds = installations.map(installation => {
|
||||||
return installation.id;
|
return installation.id;
|
||||||
@@ -445,14 +443,13 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
};
|
};
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(installations);
|
await Parse.Object.saveAll(installations);
|
||||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||||
await pushCompleted(pushStatusId);
|
await pushCompleted(pushStatusId);
|
||||||
@@ -548,16 +545,15 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = Config.get(Parse.applicationId);
|
|
||||||
const auth = {
|
|
||||||
isMaster: true,
|
|
||||||
};
|
|
||||||
await installation.save();
|
await installation.save();
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
serverURL: 'http://localhost:8378/', // server with borked URL
|
serverURL: 'http://localhost:8378/', // server with borked URL
|
||||||
push: { adapter: pushAdapter },
|
push: { adapter: pushAdapter },
|
||||||
});
|
});
|
||||||
|
const config = Config.get(Parse.applicationId);
|
||||||
|
const auth = {
|
||||||
|
isMaster: true,
|
||||||
|
};
|
||||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||||
// it is enqueued so it can take time
|
// it is enqueued so it can take time
|
||||||
await jasmine.timeout(1000);
|
await jasmine.timeout(1000);
|
||||||
@@ -580,6 +576,9 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
// $ins is invalid query
|
// $ins is invalid query
|
||||||
const where = {
|
const where = {
|
||||||
channels: {
|
channels: {
|
||||||
@@ -596,9 +595,6 @@ describe('PushController', () => {
|
|||||||
isMaster: true,
|
isMaster: true,
|
||||||
};
|
};
|
||||||
const pushController = new PushController();
|
const pushController = new PushController();
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
try {
|
try {
|
||||||
await pushController.sendPush(payload, where, config, auth);
|
await pushController.sendPush(payload, where, config, auth);
|
||||||
@@ -631,6 +627,9 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
@@ -641,9 +640,6 @@ describe('PushController', () => {
|
|||||||
$in: ['device_token_0', 'device_token_1', 'device_token_2'],
|
$in: ['device_token_0', 'device_token_1', 'device_token_2'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
const installations = [];
|
const installations = [];
|
||||||
while (installations.length != 5) {
|
while (installations.length != 5) {
|
||||||
const installation = new Parse.Object('_Installation');
|
const installation = new Parse.Object('_Installation');
|
||||||
@@ -678,7 +674,9 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
@@ -686,9 +684,6 @@ describe('PushController', () => {
|
|||||||
const where = {
|
const where = {
|
||||||
deviceType: 'ios',
|
deviceType: 'ios',
|
||||||
};
|
};
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
const installations = [];
|
const installations = [];
|
||||||
while (installations.length != 5) {
|
while (installations.length != 5) {
|
||||||
const installation = new Parse.Object('_Installation');
|
const installation = new Parse.Object('_Installation');
|
||||||
@@ -762,10 +757,6 @@ describe('PushController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not schedule push when not configured', async () => {
|
it('should not schedule push when not configured', async () => {
|
||||||
const config = Config.get(Parse.applicationId);
|
|
||||||
const auth = {
|
|
||||||
isMaster: true,
|
|
||||||
};
|
|
||||||
const pushAdapter = {
|
const pushAdapter = {
|
||||||
send: function (body, installations) {
|
send: function (body, installations) {
|
||||||
return successfulTransmissions(body, installations);
|
return successfulTransmissions(body, installations);
|
||||||
@@ -774,7 +765,13 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
|
const config = Config.get(Parse.applicationId);
|
||||||
|
const auth = {
|
||||||
|
isMaster: true,
|
||||||
|
};
|
||||||
const pushController = new PushController();
|
const pushController = new PushController();
|
||||||
const payload = {
|
const payload = {
|
||||||
data: {
|
data: {
|
||||||
@@ -793,10 +790,6 @@ describe('PushController', () => {
|
|||||||
installation.set('deviceType', 'ios');
|
installation.set('deviceType', 'ios');
|
||||||
installations.push(installation);
|
installations.push(installation);
|
||||||
}
|
}
|
||||||
|
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(installations);
|
await Parse.Object.saveAll(installations);
|
||||||
await pushController.sendPush(payload, {}, config, auth);
|
await pushController.sendPush(payload, {}, config, auth);
|
||||||
await jasmine.timeout(1000);
|
await jasmine.timeout(1000);
|
||||||
@@ -986,6 +979,10 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
spyOn(pushAdapter, 'send').and.callThrough();
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
@@ -1007,10 +1004,6 @@ describe('PushController', () => {
|
|||||||
installations[1].set('localeIdentifier', 'fr-FR');
|
installations[1].set('localeIdentifier', 'fr-FR');
|
||||||
installations[2].set('localeIdentifier', 'en-US');
|
installations[2].set('localeIdentifier', 'en-US');
|
||||||
|
|
||||||
spyOn(pushAdapter, 'send').and.callThrough();
|
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(installations);
|
await Parse.Object.saveAll(installations);
|
||||||
const pushStatusId = await sendPush(payload, where, config, auth);
|
const pushStatusId = await sendPush(payload, where, config, auth);
|
||||||
await pushCompleted(pushStatusId);
|
await pushCompleted(pushStatusId);
|
||||||
@@ -1039,7 +1032,10 @@ describe('PushController', () => {
|
|||||||
return ['ios'];
|
return ['ios'];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
spyOn(pushAdapter, 'send').and.callThrough();
|
||||||
|
await reconfigureServer({
|
||||||
|
push: { adapter: pushAdapter },
|
||||||
|
});
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
@@ -1060,10 +1056,6 @@ describe('PushController', () => {
|
|||||||
installation.set('deviceType', 'ios');
|
installation.set('deviceType', 'ios');
|
||||||
installations.push(installation);
|
installations.push(installation);
|
||||||
}
|
}
|
||||||
spyOn(pushAdapter, 'send').and.callThrough();
|
|
||||||
await reconfigureServer({
|
|
||||||
push: { adapter: pushAdapter },
|
|
||||||
});
|
|
||||||
await Parse.Object.saveAll(installations);
|
await Parse.Object.saveAll(installations);
|
||||||
|
|
||||||
// Create an audience
|
// Create an audience
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ describe('Schema Performance', function () {
|
|||||||
let config;
|
let config;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
await reconfigureServer();
|
||||||
config = Config.get('test');
|
config = Config.get('test');
|
||||||
config.schemaCache.clear();
|
|
||||||
const databaseAdapter = config.database.adapter;
|
|
||||||
await reconfigureServer({ databaseAdapter });
|
|
||||||
getAllSpy = spyOn(databaseAdapter, 'getAllClasses').and.callThrough();
|
getAllSpy = spyOn(databaseAdapter, 'getAllClasses').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -67,18 +67,22 @@ describe('Security Check Groups', () => {
|
|||||||
|
|
||||||
it('checks succeed correctly', async () => {
|
it('checks succeed correctly', async () => {
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
|
const uri = config.database.adapter._uri;
|
||||||
config.database.adapter._uri = 'protocol://user:aMoreSecur3Passwor7!@example.com';
|
config.database.adapter._uri = 'protocol://user:aMoreSecur3Passwor7!@example.com';
|
||||||
const group = new CheckGroupDatabase();
|
const group = new CheckGroupDatabase();
|
||||||
await group.run();
|
await group.run();
|
||||||
expect(group.checks()[0].checkState()).toBe(CheckState.success);
|
expect(group.checks()[0].checkState()).toBe(CheckState.success);
|
||||||
|
config.database.adapter._uri = uri;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('checks fail correctly', async () => {
|
it('checks fail correctly', async () => {
|
||||||
const config = Config.get(Parse.applicationId);
|
const config = Config.get(Parse.applicationId);
|
||||||
|
const uri = config.database.adapter._uri;
|
||||||
config.database.adapter._uri = 'protocol://user:insecure@example.com';
|
config.database.adapter._uri = 'protocol://user:insecure@example.com';
|
||||||
const group = new CheckGroupDatabase();
|
const group = new CheckGroupDatabase();
|
||||||
await group.run();
|
await group.run();
|
||||||
expect(group.checks()[0].checkState()).toBe(CheckState.fail);
|
expect(group.checks()[0].checkState()).toBe(CheckState.fail);
|
||||||
|
config.database.adapter._uri = uri;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -366,7 +366,6 @@ describe('batch', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should generate separate session for each call', async () => {
|
it('should generate separate session for each call', async () => {
|
||||||
await reconfigureServer();
|
|
||||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||||
await myObject.save({ key: 'stringField' });
|
await myObject.save({ key: 'stringField' });
|
||||||
await myObject.destroy();
|
await myObject.destroy();
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ module.exports = [
|
|||||||
describe_only_db: "readonly",
|
describe_only_db: "readonly",
|
||||||
fdescribe_only_db: "readonly",
|
fdescribe_only_db: "readonly",
|
||||||
describe_only: "readonly",
|
describe_only: "readonly",
|
||||||
|
fdescribe_only: "readonly",
|
||||||
on_db: "readonly",
|
on_db: "readonly",
|
||||||
defaultConfiguration: "readonly",
|
defaultConfiguration: "readonly",
|
||||||
range: "readonly",
|
range: "readonly",
|
||||||
|
|||||||
102
spec/helper.js
102
spec/helper.js
@@ -1,9 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const dns = require('dns');
|
const dns = require('dns');
|
||||||
const semver = require('semver');
|
const semver = require('semver');
|
||||||
|
const Parse = require('parse/node');
|
||||||
const CurrentSpecReporter = require('./support/CurrentSpecReporter.js');
|
const CurrentSpecReporter = require('./support/CurrentSpecReporter.js');
|
||||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||||
const SchemaCache = require('../lib/Adapters/Cache/SchemaCache').default;
|
const SchemaCache = require('../lib/Adapters/Cache/SchemaCache').default;
|
||||||
|
const { sleep, Connections } = require('../lib/TestUtils');
|
||||||
|
|
||||||
// Ensure localhost resolves to ipv4 address first on node v17+
|
// Ensure localhost resolves to ipv4 address first on node v17+
|
||||||
if (dns.setDefaultResultOrder) {
|
if (dns.setDefaultResultOrder) {
|
||||||
@@ -53,7 +55,6 @@ const mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'
|
|||||||
const postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
|
const postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
|
||||||
let databaseAdapter;
|
let databaseAdapter;
|
||||||
let databaseURI;
|
let databaseURI;
|
||||||
// need to bind for mocking mocha
|
|
||||||
|
|
||||||
if (process.env.PARSE_SERVER_DATABASE_ADAPTER) {
|
if (process.env.PARSE_SERVER_DATABASE_ADAPTER) {
|
||||||
databaseAdapter = JSON.parse(process.env.PARSE_SERVER_DATABASE_ADAPTER);
|
databaseAdapter = JSON.parse(process.env.PARSE_SERVER_DATABASE_ADAPTER);
|
||||||
@@ -73,7 +74,7 @@ if (process.env.PARSE_SERVER_DATABASE_ADAPTER) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const port = 8378;
|
const port = 8378;
|
||||||
|
const serverURL = `http://localhost:${port}/1`;
|
||||||
let filesAdapter;
|
let filesAdapter;
|
||||||
|
|
||||||
on_db(
|
on_db(
|
||||||
@@ -99,7 +100,7 @@ if (process.env.PARSE_SERVER_LOG_LEVEL) {
|
|||||||
// Default server configuration for tests.
|
// Default server configuration for tests.
|
||||||
const defaultConfiguration = {
|
const defaultConfiguration = {
|
||||||
filesAdapter,
|
filesAdapter,
|
||||||
serverURL: 'http://localhost:' + port + '/1',
|
serverURL,
|
||||||
databaseAdapter,
|
databaseAdapter,
|
||||||
appId: 'test',
|
appId: 'test',
|
||||||
javascriptKey: 'test',
|
javascriptKey: 'test',
|
||||||
@@ -153,34 +154,38 @@ if (silent) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
|
|
||||||
defaultConfiguration.cacheAdapter = new RedisCacheAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
const openConnections = {};
|
|
||||||
const destroyAliveConnections = function () {
|
|
||||||
for (const socketId in openConnections) {
|
|
||||||
try {
|
|
||||||
openConnections[socketId].destroy();
|
|
||||||
delete openConnections[socketId];
|
|
||||||
} catch (e) {
|
|
||||||
/* */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Set up a default API server for testing with default configuration.
|
// Set up a default API server for testing with default configuration.
|
||||||
let parseServer;
|
let parseServer;
|
||||||
let didChangeConfiguration = false;
|
let didChangeConfiguration = false;
|
||||||
|
const openConnections = new Connections();
|
||||||
|
|
||||||
|
const shutdownServer = async (_parseServer) => {
|
||||||
|
await _parseServer.handleShutdown();
|
||||||
|
// Connection close events are not immediate on node 10+, so wait a bit
|
||||||
|
await sleep(0);
|
||||||
|
expect(openConnections.count() > 0).toBeFalsy(`There were ${openConnections.count()} open connections to the server left after the test finished`);
|
||||||
|
parseServer = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
// Allows testing specific configurations of Parse Server
|
// Allows testing specific configurations of Parse Server
|
||||||
const reconfigureServer = async (changedConfiguration = {}) => {
|
const reconfigureServer = async (changedConfiguration = {}) => {
|
||||||
if (parseServer) {
|
if (parseServer) {
|
||||||
destroyAliveConnections();
|
await shutdownServer(parseServer);
|
||||||
await new Promise(resolve => parseServer.server.close(resolve));
|
|
||||||
parseServer = undefined;
|
|
||||||
return reconfigureServer(changedConfiguration);
|
return reconfigureServer(changedConfiguration);
|
||||||
}
|
}
|
||||||
didChangeConfiguration = Object.keys(changedConfiguration).length !== 0;
|
didChangeConfiguration = Object.keys(changedConfiguration).length !== 0;
|
||||||
|
databaseAdapter = new databaseAdapter.constructor({
|
||||||
|
uri: databaseURI,
|
||||||
|
collectionPrefix: 'test_',
|
||||||
|
});
|
||||||
|
defaultConfiguration.databaseAdapter = databaseAdapter;
|
||||||
|
global.databaseAdapter = databaseAdapter;
|
||||||
|
if (filesAdapter instanceof GridFSBucketAdapter) {
|
||||||
|
defaultConfiguration.filesAdapter = new GridFSBucketAdapter(mongoURI);
|
||||||
|
}
|
||||||
|
if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
|
||||||
|
defaultConfiguration.cacheAdapter = new RedisCacheAdapter();
|
||||||
|
}
|
||||||
const newConfiguration = Object.assign({}, defaultConfiguration, changedConfiguration, {
|
const newConfiguration = Object.assign({}, defaultConfiguration, changedConfiguration, {
|
||||||
mountPath: '/1',
|
mountPath: '/1',
|
||||||
port,
|
port,
|
||||||
@@ -192,39 +197,19 @@ const reconfigureServer = async (changedConfiguration = {}) => {
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
fail('should not call next');
|
fail('should not call next');
|
||||||
});
|
});
|
||||||
parseServer.liveQueryServer?.server?.on('connection', connection => {
|
openConnections.track(parseServer.server);
|
||||||
const key = `${connection.remoteAddress}:${connection.remotePort}`;
|
if (parseServer.liveQueryServer?.server && parseServer.liveQueryServer.server !== parseServer.server) {
|
||||||
openConnections[key] = connection;
|
openConnections.track(parseServer.liveQueryServer.server);
|
||||||
connection.on('close', () => {
|
}
|
||||||
delete openConnections[key];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
parseServer.server.on('connection', connection => {
|
|
||||||
const key = `${connection.remoteAddress}:${connection.remotePort}`;
|
|
||||||
openConnections[key] = connection;
|
|
||||||
connection.on('close', () => {
|
|
||||||
delete openConnections[key];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return parseServer;
|
return parseServer;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up a Parse client to talk to our test API server
|
|
||||||
const Parse = require('parse/node');
|
|
||||||
Parse.serverURL = 'http://localhost:' + port + '/1';
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
try {
|
|
||||||
Parse.User.enableUnsafeCurrentUser();
|
|
||||||
} catch (error) {
|
|
||||||
if (error !== 'You need to call Parse.initialize before using Parse.') {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await reconfigureServer();
|
await reconfigureServer();
|
||||||
|
|
||||||
Parse.initialize('test', 'test', 'test');
|
Parse.initialize('test', 'test', 'test');
|
||||||
Parse.serverURL = 'http://localhost:' + port + '/1';
|
Parse.serverURL = serverURL;
|
||||||
|
Parse.User.enableUnsafeCurrentUser();
|
||||||
|
Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
global.afterEachFn = async () => {
|
global.afterEachFn = async () => {
|
||||||
@@ -253,19 +238,7 @@ global.afterEachFn = async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await Parse.User.logOut().catch(() => {});
|
await Parse.User.logOut().catch(() => {});
|
||||||
|
|
||||||
// Connection close events are not immediate on node 10+, so wait a bit
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 0));
|
|
||||||
|
|
||||||
// After logout operations
|
|
||||||
if (Object.keys(openConnections).length > 1) {
|
|
||||||
console.warn(
|
|
||||||
`There were ${Object.keys(openConnections).length} open connections to the server left after the test finished`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await TestUtils.destroyAllDataPermanently(true);
|
await TestUtils.destroyAllDataPermanently(true);
|
||||||
SchemaCache.clear();
|
SchemaCache.clear();
|
||||||
|
|
||||||
@@ -454,6 +427,7 @@ global.mockCustomAuthenticator = mockCustomAuthenticator;
|
|||||||
global.mockFacebookAuthenticator = mockFacebookAuthenticator;
|
global.mockFacebookAuthenticator = mockFacebookAuthenticator;
|
||||||
global.databaseAdapter = databaseAdapter;
|
global.databaseAdapter = databaseAdapter;
|
||||||
global.databaseURI = databaseURI;
|
global.databaseURI = databaseURI;
|
||||||
|
global.shutdownServer = shutdownServer;
|
||||||
global.jfail = function (err) {
|
global.jfail = function (err) {
|
||||||
fail(JSON.stringify(err));
|
fail(JSON.stringify(err));
|
||||||
};
|
};
|
||||||
@@ -610,6 +584,14 @@ global.describe_only = validator => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
global.fdescribe_only = validator => {
|
||||||
|
if (validator()) {
|
||||||
|
return fdescribe;
|
||||||
|
} else {
|
||||||
|
return xdescribe;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const libraryCache = {};
|
const libraryCache = {};
|
||||||
jasmine.mockLibrary = function (library, name, mock) {
|
jasmine.mockLibrary = function (library, name, mock) {
|
||||||
const original = require(library)[name];
|
const original = require(library)[name];
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ describe('server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails if database is unreachable', async () => {
|
it('fails if database is unreachable', async () => {
|
||||||
|
spyOn(console, 'error').and.callFake(() => {});
|
||||||
const server = new ParseServer.default({
|
const server = new ParseServer.default({
|
||||||
...defaultConfiguration,
|
...defaultConfiguration,
|
||||||
databaseAdapter: new MongoStorageAdapter({
|
databaseAdapter: new MongoStorageAdapter({
|
||||||
@@ -145,7 +146,7 @@ describe('server', () => {
|
|||||||
},
|
},
|
||||||
publicServerURL: 'http://localhost:8378/1',
|
publicServerURL: 'http://localhost:8378/1',
|
||||||
};
|
};
|
||||||
expectAsync(reconfigureServer(options)).toBeRejected('MockMailAdapterConstructor');
|
await expectAsync(reconfigureServer(options)).toBeRejected('MockMailAdapterConstructor');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ const flakyTests = [
|
|||||||
"UserController sendVerificationEmail parseFrameURL provided uses parseFrameURL and includes the destination in the link parameter",
|
"UserController sendVerificationEmail parseFrameURL provided uses parseFrameURL and includes the destination in the link parameter",
|
||||||
// Expected undefined to be defined
|
// Expected undefined to be defined
|
||||||
"Email Verification Token Expiration: sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp",
|
"Email Verification Token Expiration: sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp",
|
||||||
|
// Expected 0 to be 1.
|
||||||
|
"Email Verification Token Expiration: should send a new verification email when a resend is requested and the user is UNVERIFIED",
|
||||||
];
|
];
|
||||||
|
|
||||||
/** The minimum execution time in seconds for a test to be considered slow. */
|
/** The minimum execution time in seconds for a test to be considered slow. */
|
||||||
|
|||||||
@@ -97,14 +97,22 @@ class ParseLiveQueryServer {
|
|||||||
if (this.subscriber.isOpen) {
|
if (this.subscriber.isOpen) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
...[...this.clients.values()].map(client => client.parseWebSocket.ws.close()),
|
...[...this.clients.values()].map(client => client.parseWebSocket.ws.close()),
|
||||||
this.parseWebSocketServer.close(),
|
this.parseWebSocketServer.close?.(),
|
||||||
...Array.from(this.subscriber.subscriptions.keys()).map(key =>
|
...Array.from(this.subscriber.subscriptions?.keys() || []).map(key =>
|
||||||
this.subscriber.unsubscribe(key)
|
this.subscriber.unsubscribe(key)
|
||||||
),
|
),
|
||||||
this.subscriber.close?.(),
|
this.subscriber.close?.(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
this.subscriber.isOpen = false;
|
if (typeof this.subscriber.quit === 'function') {
|
||||||
|
try {
|
||||||
|
await this.subscriber.quit();
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('PubSubAdapter error on shutdown', { error: err });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.subscriber.isOpen = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_createSubscribers() {
|
_createSubscribers() {
|
||||||
|
|||||||
@@ -45,10 +45,14 @@ import CheckRunner from './Security/CheckRunner';
|
|||||||
import Deprecator from './Deprecator/Deprecator';
|
import Deprecator from './Deprecator/Deprecator';
|
||||||
import { DefinedSchemas } from './SchemaMigrations/DefinedSchemas';
|
import { DefinedSchemas } from './SchemaMigrations/DefinedSchemas';
|
||||||
import OptionsDefinitions from './Options/Definitions';
|
import OptionsDefinitions from './Options/Definitions';
|
||||||
|
import { resolvingPromise, Connections } from './TestUtils';
|
||||||
|
|
||||||
// Mutate the Parse object to add the Cloud Code handlers
|
// Mutate the Parse object to add the Cloud Code handlers
|
||||||
addParseCloud();
|
addParseCloud();
|
||||||
|
|
||||||
|
// Track connections to destroy them on shutdown
|
||||||
|
const connections = new Connections();
|
||||||
|
|
||||||
// ParseServer works like a constructor of an express app.
|
// ParseServer works like a constructor of an express app.
|
||||||
// https://parseplatform.org/parse-server/api/master/ParseServerOptions.html
|
// https://parseplatform.org/parse-server/api/master/ParseServerOptions.html
|
||||||
class ParseServer {
|
class ParseServer {
|
||||||
@@ -214,8 +218,39 @@ class ParseServer {
|
|||||||
return this._app;
|
return this._app;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShutdown() {
|
/**
|
||||||
|
* Stops the parse server, cancels any ongoing requests and closes all connections.
|
||||||
|
*
|
||||||
|
* Currently, express doesn't shut down immediately after receiving SIGINT/SIGTERM
|
||||||
|
* if it has client connections that haven't timed out.
|
||||||
|
* (This is a known issue with node - https://github.com/nodejs/node/issues/2642)
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>} a promise that resolves when the server is stopped
|
||||||
|
*/
|
||||||
|
async handleShutdown() {
|
||||||
|
const serverClosePromise = resolvingPromise();
|
||||||
|
const liveQueryServerClosePromise = resolvingPromise();
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
this.server.close((error) => {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Error while closing parse server', error);
|
||||||
|
}
|
||||||
|
serverClosePromise.resolve();
|
||||||
|
});
|
||||||
|
if (this.liveQueryServer?.server?.close && this.liveQueryServer.server !== this.server) {
|
||||||
|
this.liveQueryServer.server.close((error) => {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Error while closing live query server', error);
|
||||||
|
}
|
||||||
|
liveQueryServerClosePromise.resolve();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
liveQueryServerClosePromise.resolve();
|
||||||
|
}
|
||||||
const { adapter: databaseAdapter } = this.config.databaseController;
|
const { adapter: databaseAdapter } = this.config.databaseController;
|
||||||
if (databaseAdapter && typeof databaseAdapter.handleShutdown === 'function') {
|
if (databaseAdapter && typeof databaseAdapter.handleShutdown === 'function') {
|
||||||
promises.push(databaseAdapter.handleShutdown());
|
promises.push(databaseAdapter.handleShutdown());
|
||||||
@@ -228,17 +263,15 @@ class ParseServer {
|
|||||||
if (cacheAdapter && typeof cacheAdapter.handleShutdown === 'function') {
|
if (cacheAdapter && typeof cacheAdapter.handleShutdown === 'function') {
|
||||||
promises.push(cacheAdapter.handleShutdown());
|
promises.push(cacheAdapter.handleShutdown());
|
||||||
}
|
}
|
||||||
if (this.liveQueryServer?.server?.close) {
|
|
||||||
promises.push(new Promise(resolve => this.liveQueryServer.server.close(resolve)));
|
|
||||||
}
|
|
||||||
if (this.liveQueryServer) {
|
if (this.liveQueryServer) {
|
||||||
promises.push(this.liveQueryServer.shutdown());
|
promises.push(this.liveQueryServer.shutdown());
|
||||||
}
|
}
|
||||||
return (promises.length > 0 ? Promise.all(promises) : Promise.resolve()).then(() => {
|
await Promise.all(promises);
|
||||||
if (this.config.serverCloseComplete) {
|
connections.destroyAll();
|
||||||
this.config.serverCloseComplete();
|
await Promise.all([serverClosePromise, liveQueryServerClosePromise]);
|
||||||
}
|
if (this.config.serverCloseComplete) {
|
||||||
});
|
this.config.serverCloseComplete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -419,6 +452,7 @@ class ParseServer {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.server = server;
|
this.server = server;
|
||||||
|
connections.track(server);
|
||||||
|
|
||||||
if (options.startLiveQueryServer || options.liveQueryServerOptions) {
|
if (options.startLiveQueryServer || options.liveQueryServerOptions) {
|
||||||
this.liveQueryServer = await ParseServer.createLiveQueryServer(
|
this.liveQueryServer = await ParseServer.createLiveQueryServer(
|
||||||
@@ -426,6 +460,9 @@ class ParseServer {
|
|||||||
options.liveQueryServerOptions,
|
options.liveQueryServerOptions,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
if (this.liveQueryServer.server !== this.server) {
|
||||||
|
connections.track(this.liveQueryServer.server);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (options.trustProxy) {
|
if (options.trustProxy) {
|
||||||
app.set('trust proxy', options.trustProxy);
|
app.set('trust proxy', options.trustProxy);
|
||||||
@@ -600,32 +637,8 @@ function injectDefaults(options: ParseServerOptions) {
|
|||||||
// Those can't be tested as it requires a subprocess
|
// Those can't be tested as it requires a subprocess
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
function configureListeners(parseServer) {
|
function configureListeners(parseServer) {
|
||||||
const server = parseServer.server;
|
|
||||||
const sockets = {};
|
|
||||||
/* Currently, express doesn't shut down immediately after receiving SIGINT/SIGTERM if it has client connections that haven't timed out. (This is a known issue with node - https://github.com/nodejs/node/issues/2642)
|
|
||||||
This function, along with `destroyAliveConnections()`, intend to fix this behavior such that parse server will close all open connections and initiate the shutdown process as soon as it receives a SIGINT/SIGTERM signal. */
|
|
||||||
server.on('connection', socket => {
|
|
||||||
const socketId = socket.remoteAddress + ':' + socket.remotePort;
|
|
||||||
sockets[socketId] = socket;
|
|
||||||
socket.on('close', () => {
|
|
||||||
delete sockets[socketId];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const destroyAliveConnections = function () {
|
|
||||||
for (const socketId in sockets) {
|
|
||||||
try {
|
|
||||||
sockets[socketId].destroy();
|
|
||||||
} catch (e) {
|
|
||||||
/* */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleShutdown = function () {
|
const handleShutdown = function () {
|
||||||
process.stdout.write('Termination signal received. Shutting down.');
|
process.stdout.write('Termination signal received. Shutting down.');
|
||||||
destroyAliveConnections();
|
|
||||||
server.close();
|
|
||||||
parseServer.handleShutdown();
|
parseServer.handleShutdown();
|
||||||
};
|
};
|
||||||
process.on('SIGTERM', handleShutdown);
|
process.on('SIGTERM', handleShutdown);
|
||||||
|
|||||||
@@ -42,3 +42,42 @@ export function resolvingPromise() {
|
|||||||
export function sleep(ms) {
|
export function sleep(ms) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getConnectionsCount(server) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
server.getConnections((err, count) => {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(count);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Connections {
|
||||||
|
constructor() {
|
||||||
|
this.sockets = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
track(server) {
|
||||||
|
server.on('connection', socket => {
|
||||||
|
this.sockets.add(socket);
|
||||||
|
socket.on('close', () => {
|
||||||
|
this.sockets.delete(socket);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyAll() {
|
||||||
|
for (const socket of this.sockets.values()) {
|
||||||
|
socket.destroy();
|
||||||
|
}
|
||||||
|
this.sockets.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
count() {
|
||||||
|
return this.sockets.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user