Merge pull request #980 from ParsePlatform/flovilmart.ParseServerClass
ParseServer is a class
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
var request = require('request');
|
||||
var parseServerPackage = require('../package.json');
|
||||
var MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
|
||||
var ParseServer = require("../src/index");
|
||||
var express = require('express');
|
||||
|
||||
describe('server', () => {
|
||||
it('requires a master key and app id', done => {
|
||||
@@ -168,4 +170,62 @@ describe('server', () => {
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('can create a parse-server', done => {
|
||||
var parseServer = new ParseServer.default({
|
||||
appId: "aTestApp",
|
||||
masterKey: "aTestMasterKey",
|
||||
serverURL: "http://localhost:12666/parse",
|
||||
databaseURI: 'mongodb://localhost:27017/aTestApp'
|
||||
});
|
||||
|
||||
expect(Parse.applicationId).toEqual("aTestApp");
|
||||
var app = express();
|
||||
app.use('/parse', parseServer.app);
|
||||
|
||||
var server = app.listen(12666);
|
||||
var obj = new Parse.Object("AnObject");
|
||||
var objId;
|
||||
obj.save().then((obj) => {
|
||||
objId = obj.id;
|
||||
var q = new Parse.Query("AnObject");
|
||||
return q.first();
|
||||
}).then((obj) => {
|
||||
expect(obj.id).toEqual(objId);
|
||||
server.close();
|
||||
done();
|
||||
}).fail((err) => {
|
||||
server.close();
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('can create a parse-server', done => {
|
||||
var parseServer = ParseServer.ParseServer({
|
||||
appId: "anOtherTestApp",
|
||||
masterKey: "anOtherTestMasterKey",
|
||||
serverURL: "http://localhost:12667/parse",
|
||||
databaseURI: 'mongodb://localhost:27017/anotherTstApp'
|
||||
});
|
||||
|
||||
expect(Parse.applicationId).toEqual("anOtherTestApp");
|
||||
var app = express();
|
||||
app.use('/parse', parseServer);
|
||||
|
||||
var server = app.listen(12667);
|
||||
var obj = new Parse.Object("AnObject");
|
||||
var objId;
|
||||
obj.save().then((obj) => {
|
||||
objId = obj.id;
|
||||
var q = new Parse.Query("AnObject");
|
||||
return q.first();
|
||||
}).then((obj) => {
|
||||
expect(obj.id).toEqual(objId);
|
||||
server.close();
|
||||
done();
|
||||
}).fail((err) => {
|
||||
server.close();
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
374
src/index.js
374
src/index.js
@@ -79,187 +79,205 @@ addParseCloud();
|
||||
// "javascriptKey": optional key from Parse dashboard
|
||||
// "push": optional key from configure push
|
||||
|
||||
function ParseServer({
|
||||
appId = requiredParameter('You must provide an appId!'),
|
||||
masterKey = requiredParameter('You must provide a masterKey!'),
|
||||
appName,
|
||||
databaseAdapter,
|
||||
filesAdapter,
|
||||
push,
|
||||
loggerAdapter,
|
||||
databaseURI = DatabaseAdapter.defaultDatabaseURI,
|
||||
databaseOptions,
|
||||
cloud,
|
||||
collectionPrefix = '',
|
||||
clientKey,
|
||||
javascriptKey,
|
||||
dotNetKey,
|
||||
restAPIKey,
|
||||
fileKey = 'invalid-file-key',
|
||||
facebookAppIds = [],
|
||||
enableAnonymousUsers = true,
|
||||
allowClientClassCreation = true,
|
||||
oauth = {},
|
||||
serverURL = requiredParameter('You must provide a serverURL!'),
|
||||
maxUploadSize = '20mb',
|
||||
verifyUserEmails = false,
|
||||
emailAdapter,
|
||||
publicServerURL,
|
||||
customPages = {
|
||||
invalidLink: undefined,
|
||||
verifyEmailSuccess: undefined,
|
||||
choosePassword: undefined,
|
||||
passwordResetSuccess: undefined
|
||||
},
|
||||
liveQuery = {}
|
||||
}) {
|
||||
setFeature('serverVersion', parseServerPackage.version);
|
||||
// Initialize the node client SDK automatically
|
||||
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
|
||||
Parse.serverURL = serverURL;
|
||||
export default class ParseServer {
|
||||
|
||||
if (databaseAdapter) {
|
||||
DatabaseAdapter.setAdapter(databaseAdapter);
|
||||
}
|
||||
constructor({
|
||||
appId = requiredParameter('You must provide an appId!'),
|
||||
masterKey = requiredParameter('You must provide a masterKey!'),
|
||||
appName,
|
||||
databaseAdapter,
|
||||
filesAdapter,
|
||||
push,
|
||||
loggerAdapter,
|
||||
databaseURI = DatabaseAdapter.defaultDatabaseURI,
|
||||
databaseOptions,
|
||||
cloud,
|
||||
collectionPrefix = '',
|
||||
clientKey,
|
||||
javascriptKey,
|
||||
dotNetKey,
|
||||
restAPIKey,
|
||||
fileKey = 'invalid-file-key',
|
||||
facebookAppIds = [],
|
||||
enableAnonymousUsers = true,
|
||||
allowClientClassCreation = true,
|
||||
oauth = {},
|
||||
serverURL = requiredParameter('You must provide a serverURL!'),
|
||||
maxUploadSize = '20mb',
|
||||
verifyUserEmails = false,
|
||||
emailAdapter,
|
||||
publicServerURL,
|
||||
customPages = {
|
||||
invalidLink: undefined,
|
||||
verifyEmailSuccess: undefined,
|
||||
choosePassword: undefined,
|
||||
passwordResetSuccess: undefined
|
||||
},
|
||||
liveQuery = {}
|
||||
}) {
|
||||
setFeature('serverVersion', parseServerPackage.version);
|
||||
// Initialize the node client SDK automatically
|
||||
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
|
||||
Parse.serverURL = serverURL;
|
||||
|
||||
if (databaseURI) {
|
||||
DatabaseAdapter.setAppDatabaseURI(appId, databaseURI);
|
||||
}
|
||||
|
||||
if (databaseOptions) {
|
||||
DatabaseAdapter.setAppDatabaseOptions(appId, databaseOptions);
|
||||
}
|
||||
|
||||
if (cloud) {
|
||||
addParseCloud();
|
||||
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";
|
||||
if (databaseAdapter) {
|
||||
DatabaseAdapter.setAdapter(databaseAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
const filesControllerAdapter = loadAdapter(filesAdapter, () => {
|
||||
return new GridStoreAdapter(databaseURI);
|
||||
});
|
||||
// Pass the push options too as it works with the default
|
||||
const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push);
|
||||
const loggerControllerAdapter = loadAdapter(loggerAdapter, FileLoggerAdapter);
|
||||
const emailControllerAdapter = loadAdapter(emailAdapter);
|
||||
// We pass the options and the base class for the adatper,
|
||||
// Note that passing an instance would work too
|
||||
const filesController = new FilesController(filesControllerAdapter, appId);
|
||||
const pushController = new PushController(pushControllerAdapter, appId);
|
||||
const loggerController = new LoggerController(loggerControllerAdapter, appId);
|
||||
const hooksController = new HooksController(appId, collectionPrefix);
|
||||
const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails });
|
||||
const liveQueryController = new LiveQueryController(liveQuery);
|
||||
if (databaseOptions) {
|
||||
DatabaseAdapter.setAppDatabaseOptions(appId, databaseOptions);
|
||||
}
|
||||
|
||||
if (databaseURI) {
|
||||
DatabaseAdapter.setAppDatabaseURI(appId, databaseURI);
|
||||
}
|
||||
|
||||
cache.apps.set(appId, {
|
||||
masterKey: masterKey,
|
||||
serverURL: serverURL,
|
||||
collectionPrefix: collectionPrefix,
|
||||
clientKey: clientKey,
|
||||
javascriptKey: javascriptKey,
|
||||
dotNetKey: dotNetKey,
|
||||
restAPIKey: restAPIKey,
|
||||
fileKey: fileKey,
|
||||
facebookAppIds: facebookAppIds,
|
||||
filesController: filesController,
|
||||
pushController: pushController,
|
||||
loggerController: loggerController,
|
||||
hooksController: hooksController,
|
||||
userController: userController,
|
||||
verifyUserEmails: verifyUserEmails,
|
||||
allowClientClassCreation: allowClientClassCreation,
|
||||
authDataManager: authDataManager(oauth, enableAnonymousUsers),
|
||||
appName: appName,
|
||||
publicServerURL: publicServerURL,
|
||||
customPages: customPages,
|
||||
liveQueryController: liveQueryController
|
||||
});
|
||||
|
||||
// To maintain compatibility. TODO: Remove in some version that breaks backwards compatability
|
||||
if (process.env.FACEBOOK_APP_ID) {
|
||||
cache.apps.get(appId)['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
|
||||
}
|
||||
|
||||
Config.validate(cache.apps.get(appId));
|
||||
|
||||
// This app serves the Parse API directly.
|
||||
// It's the equivalent of https://api.parse.com/1 in the hosted Parse API.
|
||||
var api = express();
|
||||
//api.use("/apps", express.static(__dirname + "/public"));
|
||||
// File handling needs to be before default middlewares are applied
|
||||
api.use('/', middlewares.allowCrossDomain, new FilesRouter().getExpressRouter({
|
||||
maxUploadSize: maxUploadSize
|
||||
}));
|
||||
|
||||
api.use('/', bodyParser.urlencoded({extended: false}), new PublicAPIRouter().expressApp());
|
||||
|
||||
// TODO: separate this from the regular ParseServer object
|
||||
if (process.env.TESTING == 1) {
|
||||
api.use('/', require('./testing-routes').router);
|
||||
}
|
||||
|
||||
api.use(bodyParser.json({ 'type': '*/*' , limit: maxUploadSize }));
|
||||
api.use(middlewares.allowCrossDomain);
|
||||
api.use(middlewares.allowMethodOverride);
|
||||
api.use(middlewares.handleParseHeaders);
|
||||
|
||||
let routers = [
|
||||
new ClassesRouter(),
|
||||
new UsersRouter(),
|
||||
new SessionsRouter(),
|
||||
new RolesRouter(),
|
||||
new AnalyticsRouter(),
|
||||
new InstallationsRouter(),
|
||||
new FunctionsRouter(),
|
||||
new SchemasRouter(),
|
||||
new PushRouter(),
|
||||
new LogsRouter(),
|
||||
new IAPValidationRouter(),
|
||||
new FeaturesRouter(),
|
||||
];
|
||||
|
||||
if (process.env.PARSE_EXPERIMENTAL_CONFIG_ENABLED || process.env.TESTING) {
|
||||
routers.push(new GlobalConfigRouter());
|
||||
}
|
||||
|
||||
if (process.env.PARSE_EXPERIMENTAL_HOOKS_ENABLED || process.env.TESTING) {
|
||||
routers.push(new HooksRouter());
|
||||
}
|
||||
|
||||
let routes = routers.reduce((memo, router) => {
|
||||
return memo.concat(router.routes);
|
||||
}, []);
|
||||
|
||||
let appRouter = new PromiseRouter(routes);
|
||||
|
||||
batch.mountOnto(appRouter);
|
||||
|
||||
api.use(appRouter.expressApp());
|
||||
|
||||
api.use(middlewares.handleParseErrors);
|
||||
|
||||
//This causes tests to spew some useless warnings, so disable in test
|
||||
if (!process.env.TESTING) {
|
||||
process.on('uncaughtException', (err) => {
|
||||
if( err.code === "EADDRINUSE" ) { // user-friendly message for this common error
|
||||
console.log(`Unable to listen on port ${err.port}. The port is already in use.`);
|
||||
process.exit(0);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
if (cloud) {
|
||||
addParseCloud();
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const filesControllerAdapter = loadAdapter(filesAdapter, () => {
|
||||
return new GridStoreAdapter(databaseURI);
|
||||
});
|
||||
}
|
||||
hooksController.load();
|
||||
// Pass the push options too as it works with the default
|
||||
const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push);
|
||||
const loggerControllerAdapter = loadAdapter(loggerAdapter, FileLoggerAdapter);
|
||||
const emailControllerAdapter = loadAdapter(emailAdapter);
|
||||
// We pass the options and the base class for the adatper,
|
||||
// Note that passing an instance would work too
|
||||
const filesController = new FilesController(filesControllerAdapter, appId);
|
||||
const pushController = new PushController(pushControllerAdapter, appId);
|
||||
const loggerController = new LoggerController(loggerControllerAdapter, appId);
|
||||
const hooksController = new HooksController(appId, collectionPrefix);
|
||||
const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails });
|
||||
const liveQueryController = new LiveQueryController(liveQuery);
|
||||
|
||||
return api;
|
||||
cache.apps.set(appId, {
|
||||
masterKey: masterKey,
|
||||
serverURL: serverURL,
|
||||
collectionPrefix: collectionPrefix,
|
||||
clientKey: clientKey,
|
||||
javascriptKey: javascriptKey,
|
||||
dotNetKey: dotNetKey,
|
||||
restAPIKey: restAPIKey,
|
||||
fileKey: fileKey,
|
||||
facebookAppIds: facebookAppIds,
|
||||
filesController: filesController,
|
||||
pushController: pushController,
|
||||
loggerController: loggerController,
|
||||
hooksController: hooksController,
|
||||
userController: userController,
|
||||
verifyUserEmails: verifyUserEmails,
|
||||
allowClientClassCreation: allowClientClassCreation,
|
||||
authDataManager: authDataManager(oauth, enableAnonymousUsers),
|
||||
appName: appName,
|
||||
publicServerURL: publicServerURL,
|
||||
customPages: customPages,
|
||||
maxUploadSize: maxUploadSize,
|
||||
liveQueryController: liveQueryController
|
||||
});
|
||||
|
||||
// To maintain compatibility. TODO: Remove in some version that breaks backwards compatability
|
||||
if (process.env.FACEBOOK_APP_ID) {
|
||||
cache.apps.get(appId)['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
|
||||
}
|
||||
|
||||
Config.validate(cache.apps.get(appId));
|
||||
this.config = cache.apps.get(appId);
|
||||
hooksController.load();
|
||||
}
|
||||
|
||||
get app() {
|
||||
return ParseServer.app(this.config);
|
||||
}
|
||||
|
||||
static app({maxUploadSize = '20mb'}) {
|
||||
// This app serves the Parse API directly.
|
||||
// It's the equivalent of https://api.parse.com/1 in the hosted Parse API.
|
||||
var api = express();
|
||||
//api.use("/apps", express.static(__dirname + "/public"));
|
||||
// File handling needs to be before default middlewares are applied
|
||||
api.use('/', middlewares.allowCrossDomain, new FilesRouter().getExpressRouter({
|
||||
maxUploadSize: maxUploadSize
|
||||
}));
|
||||
|
||||
api.use('/', bodyParser.urlencoded({extended: false}), new PublicAPIRouter().expressApp());
|
||||
|
||||
// TODO: separate this from the regular ParseServer object
|
||||
if (process.env.TESTING == 1) {
|
||||
api.use('/', require('./testing-routes').router);
|
||||
}
|
||||
|
||||
api.use(bodyParser.json({ 'type': '*/*' , limit: maxUploadSize }));
|
||||
api.use(middlewares.allowCrossDomain);
|
||||
api.use(middlewares.allowMethodOverride);
|
||||
api.use(middlewares.handleParseHeaders);
|
||||
|
||||
let routers = [
|
||||
new ClassesRouter(),
|
||||
new UsersRouter(),
|
||||
new SessionsRouter(),
|
||||
new RolesRouter(),
|
||||
new AnalyticsRouter(),
|
||||
new InstallationsRouter(),
|
||||
new FunctionsRouter(),
|
||||
new SchemasRouter(),
|
||||
new PushRouter(),
|
||||
new LogsRouter(),
|
||||
new IAPValidationRouter(),
|
||||
new FeaturesRouter(),
|
||||
];
|
||||
|
||||
if (process.env.PARSE_EXPERIMENTAL_CONFIG_ENABLED || process.env.TESTING) {
|
||||
routers.push(new GlobalConfigRouter());
|
||||
}
|
||||
|
||||
if (process.env.PARSE_EXPERIMENTAL_HOOKS_ENABLED || process.env.TESTING) {
|
||||
routers.push(new HooksRouter());
|
||||
}
|
||||
|
||||
let routes = routers.reduce((memo, router) => {
|
||||
return memo.concat(router.routes);
|
||||
}, []);
|
||||
|
||||
let appRouter = new PromiseRouter(routes);
|
||||
|
||||
batch.mountOnto(appRouter);
|
||||
|
||||
api.use(appRouter.expressApp());
|
||||
|
||||
api.use(middlewares.handleParseErrors);
|
||||
|
||||
//This causes tests to spew some useless warnings, so disable in test
|
||||
if (!process.env.TESTING) {
|
||||
process.on('uncaughtException', (err) => {
|
||||
if ( err.code === "EADDRINUSE" ) { // user-friendly message for this common error
|
||||
console.log(`Unable to listen on port ${err.port}. The port is already in use.`);
|
||||
process.exit(0);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
||||
static ParseServer(options) {
|
||||
let server = new ParseServer(options);
|
||||
return server.app;
|
||||
}
|
||||
|
||||
static createLiveQueryServer(httpServer, config) {
|
||||
return new ParseLiveQueryServer(httpServer, config);
|
||||
}
|
||||
}
|
||||
|
||||
function addParseCloud() {
|
||||
@@ -268,13 +286,9 @@ function addParseCloud() {
|
||||
global.Parse = Parse;
|
||||
}
|
||||
|
||||
ParseServer.createLiveQueryServer = function(httpServer, config) {
|
||||
return new ParseLiveQueryServer(httpServer, config);
|
||||
let runServer = function(options) {
|
||||
return ParseServer.ParseServer(options);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ParseServer: ParseServer,
|
||||
S3Adapter: S3Adapter,
|
||||
GCSAdapter: GCSAdapter,
|
||||
FileSystemAdapter: FileSystemAdapter
|
||||
};
|
||||
export { S3Adapter, GCSAdapter, FileSystemAdapter };
|
||||
export { runServer as ParseServer };
|
||||
|
||||
Reference in New Issue
Block a user