Skip to content

Add ability to test multiple server configurations #498

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

Merged
merged 4 commits into from
Feb 20, 2016
Merged
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
30 changes: 26 additions & 4 deletions spec/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ var DatabaseAdapter = require('../src/DatabaseAdapter');
var express = require('express');
var facebook = require('../src/oauth/facebook');
var ParseServer = require('../src/index').ParseServer;
var DatabaseAdapter = require('../src/DatabaseAdapter');

var databaseURI = process.env.DATABASE_URI;
var cloudMain = process.env.CLOUD_CODE_MAIN || './cloud/main.js';
var port = 8378;

// Set up an API server for testing
var api = new ParseServer({
// Default server configuration for tests.
var defaultConfiguration = {
databaseURI: databaseURI,
cloud: cloudMain,
serverURL: 'http://localhost:' + port + '/1',
appId: 'test',
javascriptKey: 'test',
dotNetKey: 'windows',
Expand All @@ -29,13 +32,29 @@ var api = new ParseServer({
module: "../spec/myoauth" // relative path as it's run from src
}
}
});
};

// Set up a default API server for testing with default configuration.
var api = new ParseServer(defaultConfiguration);
var app = express();
app.use('/1', api);
var port = 8378;
var server = app.listen(port);

// Prevent reinitializing the server from clobbering Cloud Code
delete defaultConfiguration.cloud;

// Allows testing specific configurations of Parse Server
var setServerConfiguration = configuration => {
api = new ParseServer(configuration);
app = express();
app.use('/1', api);
cache.clearCache();
server.close();
server = app.listen(port);
}

var restoreServerConfiguration = () => setServerConfiguration(defaultConfiguration);

// Set up a Parse client to talk to our test API server
var Parse = require('parse/node');
Parse.serverURL = 'http://localhost:' + port + '/1';
Expand All @@ -51,9 +70,11 @@ beforeEach(function(done) {
});

afterEach(function(done) {
restoreServerConfiguration();
Parse.User.logOut().then(() => {
return clearData();
}).then(() => {
DatabaseAdapter.clearDatabaseURIs();
done();
}, (error) => {
console.log('error in clearData', error);
Expand Down Expand Up @@ -221,3 +242,4 @@ global.expectError = expectError;
global.arrayContains = arrayContains;
global.jequal = jequal;
global.range = range;
global.setServerConfiguration = setServerConfiguration;
39 changes: 39 additions & 0 deletions spec/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
var request = require('request');

describe('server', () => {
it('requires a master key and app id', done => {
expect(setServerConfiguration.bind(undefined, { masterKey: 'mykey' })).toThrow('You must provide an appId and masterKey!');
expect(setServerConfiguration.bind(undefined, { appId: 'myId' })).toThrow('You must provide an appId and masterKey!');
done();
});

it('fails if database is unreachable', done => {
setServerConfiguration({
databaseURI: 'mongodb://fake:[email protected]:43605/drew3',
serverURL: 'http://localhost:8378/1',
appId: 'test',
javascriptKey: 'test',
dotNetKey: 'windows',
clientKey: 'client',
restAPIKey: 'rest',
masterKey: 'test',
collectionPrefix: 'test_',
fileKey: 'test',
});
//Need to use rest api because saving via JS SDK results in fail() not getting called
request.post({
url: 'http://localhost:8378/1/classes/NewClass',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
body: {},
json: true,
}, (error, response, body) => {
expect(response.statusCode).toEqual(500);
expect(body.code).toEqual(1);
expect(body.message).toEqual('Internal server error.');
done();
});
});
});
9 changes: 8 additions & 1 deletion src/DatabaseAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ function setAppDatabaseURI(appId, uri) {
appDatabaseURIs[appId] = uri;
}

//Used by tests
function clearDatabaseURIs() {
appDatabaseURIs = {};
dbConnections = {};
}

function getDatabaseConnection(appId) {
if (dbConnections[appId]) {
return dbConnections[appId];
Expand All @@ -52,5 +58,6 @@ module.exports = {
getDatabaseConnection: getDatabaseConnection,
setAdapter: setAdapter,
setDatabaseURI: setDatabaseURI,
setAppDatabaseURI: setAppDatabaseURI
setAppDatabaseURI: setAppDatabaseURI,
clearDatabaseURIs: clearDatabaseURIs,
};
10 changes: 9 additions & 1 deletion src/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ function clearUser(sessionToken) {
delete users[sessionToken];
}

//So far used only in tests
function clearCache() {
apps = {};
stats = {};
users = {};
}

module.exports = {
apps: apps,
stats: stats,
Expand All @@ -33,5 +40,6 @@ module.exports = {
updateStat: updateStat,
clearUser: clearUser,
getUser: getUser,
setUser: setUser
setUser: setUser,
clearCache: clearCache,
};
101 changes: 56 additions & 45 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ var batch = require('./batch'),
PromiseRouter = require('./PromiseRouter'),
httpRequest = require('./httpRequest');

import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
import { S3Adapter } from './Adapters/Files/S3Adapter';
import { FilesController } from './Controllers/FilesController';
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
import { S3Adapter } from './Adapters/Files/S3Adapter';
import { FilesController } from './Controllers/FilesController';

import ParsePushAdapter from './Adapters/Push/ParsePushAdapter';
import { PushController } from './Controllers/PushController';
import ParsePushAdapter from './Adapters/Push/ParsePushAdapter';
import { PushController } from './Controllers/PushController';

import { ClassesRouter } from './Routers/ClassesRouter';
import { ClassesRouter } from './Routers/ClassesRouter';
import { InstallationsRouter } from './Routers/InstallationsRouter';
import { UsersRouter } from './Routers/UsersRouter';
import { SessionsRouter } from './Routers/SessionsRouter';
import { RolesRouter } from './Routers/RolesRouter';
import { UsersRouter } from './Routers/UsersRouter';
import { SessionsRouter } from './Routers/SessionsRouter';
import { RolesRouter } from './Routers/RolesRouter';

import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
import { LoggerController } from './Controllers/LoggerController';
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
import { LoggerController } from './Controllers/LoggerController';

// Mutate the Parse object to add the Cloud Code handlers
addParseCloud();
Expand Down Expand Up @@ -54,69 +54,81 @@ addParseCloud();
// "javascriptKey": optional key from Parse dashboard
// "push": optional key from configure push

function ParseServer(args) {
if (!args.appId || !args.masterKey) {
function ParseServer({
appId,
masterKey,
databaseAdapter,
filesAdapter = new GridStoreAdapter(),
push,
loggerAdapter = new FileLoggerAdapter(),
databaseURI,
cloud,
collectionPrefix = '',
clientKey = '',
javascriptKey = '',
dotNetKey = '',
restAPIKey = '',
fileKey = 'invalid-file-key',
facebookAppIds = [],
enableAnonymousUsers = true,
oauth = {},
serverURL = '',
}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that an accept? :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that syntax, learning ES6 at the same time you know... js/node is only my part-time pleasure.

if (!appId || !masterKey) {
throw 'You must provide an appId and masterKey!';
}

if (args.databaseAdapter) {
DatabaseAdapter.setAdapter(args.databaseAdapter);
if (databaseAdapter) {
DatabaseAdapter.setAdapter(databaseAdapter);
}

// Make files adapter
let filesAdapter = args.filesAdapter || new GridStoreAdapter();

// Make push adapter
let pushConfig = args.push;
let pushConfig = push;
let pushAdapter;
if (pushConfig && pushConfig.adapter) {
pushAdapter = pushConfig.adapter;
} else if (pushConfig) {
pushAdapter = new ParsePushAdapter(pushConfig)
}

// Make logger adapter
let loggerAdapter = args.loggerAdapter || new FileLoggerAdapter();

if (args.databaseURI) {
DatabaseAdapter.setAppDatabaseURI(args.appId, args.databaseURI);
if (databaseURI) {
DatabaseAdapter.setAppDatabaseURI(appId, databaseURI);
}
if (args.cloud) {
if (cloud) {
addParseCloud();
if (typeof args.cloud === 'function') {
args.cloud(Parse)
} else if (typeof args.cloud === 'string') {
require(args.cloud);
if (typeof cloud === 'function') {
cloud(Parse)
} else if (typeof cloud === 'string') {
require(cloud);
} else {
throw "argument 'cloud' must either be a string or a function";
}

}

let filesController = new FilesController(filesAdapter);

cache.apps[args.appId] = {
masterKey: args.masterKey,
collectionPrefix: args.collectionPrefix || '',
clientKey: args.clientKey || '',
javascriptKey: args.javascriptKey || '',
dotNetKey: args.dotNetKey || '',
restAPIKey: args.restAPIKey || '',
fileKey: args.fileKey || 'invalid-file-key',
facebookAppIds: args.facebookAppIds || [],
cache.apps[appId] = {
masterKey: masterKey,
collectionPrefix: collectionPrefix,
clientKey: clientKey,
javascriptKey: javascriptKey,
dotNetKey: dotNetKey,
restAPIKey: restAPIKey,
fileKey: fileKey,
facebookAppIds: facebookAppIds,
filesController: filesController,
enableAnonymousUsers: args.enableAnonymousUsers || true,
oauth: args.oauth || {},
enableAnonymousUsers: enableAnonymousUsers,
oauth: oauth,
};

// To maintain compatibility. TODO: Remove in v2.1
if (process.env.FACEBOOK_APP_ID) {
cache.apps[args.appId]['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
cache.apps[appId]['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
}

// Initialize the node client SDK automatically
Parse.initialize(args.appId, args.javascriptKey || '', args.masterKey);
Parse.serverURL = args.serverURL || '';
Parse.initialize(appId, javascriptKey, masterKey);
Parse.serverURL = serverURL;

// This app serves the Parse API directly.
// It's the equivalent of https://api.parse.com/1 in the hosted Parse API.
Expand All @@ -127,7 +139,6 @@ function ParseServer(args) {

// TODO: separate this from the regular ParseServer object
if (process.env.TESTING == 1) {
console.log('enabling integration testing-routes');
api.use('/', require('./testing-routes').router);
}

Expand Down