diff --git a/spec/DatabaseController.spec.js b/spec/DatabaseController.spec.js index d7f523715c..1b4fd11c41 100644 --- a/spec/DatabaseController.spec.js +++ b/spec/DatabaseController.spec.js @@ -6,11 +6,10 @@ let MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAda describe('DatabaseController', () => { it('can be constructed', done => { let adapter = new MongoStorageAdapter({ - uri: 'mongodb://localhost:27017/test' - }); - let databaseController = new DatabaseController(adapter, { - collectionPrefix: 'test_' + uri: 'mongodb://localhost:27017/test', + collectionPrefix: 'test_', }); + let databaseController = new DatabaseController(adapter); databaseController.connect().then(done, error => { console.log('error', error.stack); fail(); diff --git a/src/Adapters/Files/GridStoreAdapter.js b/src/Adapters/Files/GridStoreAdapter.js index 22d5680d46..1d469ee07c 100644 --- a/src/Adapters/Files/GridStoreAdapter.js +++ b/src/Adapters/Files/GridStoreAdapter.js @@ -7,13 +7,15 @@ */ import { MongoClient, GridStore, Db} from 'mongodb'; -import { FilesAdapter } from './FilesAdapter'; +import { FilesAdapter } from './FilesAdapter'; + +const DefaultMongoURI = 'mongodb://localhost:27017/parse'; export class GridStoreAdapter extends FilesAdapter { _databaseURI: string; _connectionPromise: Promise; - constructor(mongoDatabaseURI: string) { + constructor(mongoDatabaseURI = DefaultMongoURI) { super(); this._databaseURI = mongoDatabaseURI; this._connect(); diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index fba51cf47c..1de5fa3aa6 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -7,6 +7,7 @@ let mongodb = require('mongodb'); let MongoClient = mongodb.MongoClient; const MongoSchemaCollectionName = '_SCHEMA'; +const DefaultMongoURI = 'mongodb://localhost:27017/parse'; export class MongoStorageAdapter { // Private @@ -18,7 +19,7 @@ export class MongoStorageAdapter { database; constructor({ - uri, + uri = DefaultMongoURI, collectionPrefix = '', mongoOptions = {}, }) { @@ -50,29 +51,30 @@ export class MongoStorageAdapter { adaptiveCollection(name: string) { return this.connect() - .then(() => this.database.collection(name)) + .then(() => this.database.collection(this._collectionPrefix + name)) .then(rawCollection => new MongoCollection(rawCollection)); } - schemaCollection(collectionPrefix: string) { + schemaCollection() { return this.connect() - .then(() => this.adaptiveCollection(collectionPrefix + MongoSchemaCollectionName)) + .then(() => this.adaptiveCollection(this._collectionPrefix + MongoSchemaCollectionName)) .then(collection => new MongoSchemaCollection(collection)); } collectionExists(name: string) { return this.connect().then(() => { - return this.database.listCollections({ name: name }).toArray(); + return this.database.listCollections({ name: this._collectionPrefix + name }).toArray(); }).then(collections => { return collections.length > 0; }); } dropCollection(name: string) { - return this.collection(name).then(collection => collection.drop()); + return this.collection(this._collectionPrefix + name).then(collection => collection.drop()); } + // Used for testing only right now. - collectionsContaining(match: string) { + allCollections() { return this.connect().then(() => { return this.database.collections(); }).then(collections => { @@ -80,7 +82,7 @@ export class MongoStorageAdapter { if (collection.namespace.match(/\.system\./)) { return false; } - return (collection.collectionName.indexOf(match) == 0); + return (collection.collectionName.indexOf(this._collectionPrefix) == 0); }); }); } @@ -105,13 +107,7 @@ export class MongoStorageAdapter { // may do so. // Returns a Promise. - - // This function currently accepts the collectionPrefix and adaptive collection as a paramater because it isn't - // actually capable of determining the location of it's own _SCHEMA collection without having - // the collectionPrefix. Also, Schemas.js, the caller of this function, only stores the collection - // itself, and not the prefix. Eventually Parse Server won't care what a SchemaCollection is and - // will just tell the DB adapter to do things and it will do them. - deleteFields(className: string, fieldNames, pointerFieldNames, collectionPrefix, adaptiveCollection) { + deleteFields(className: string, fieldNames, pointerFieldNames) { const nonPointerFieldNames = _.difference(fieldNames, pointerFieldNames); const mongoFormatNames = nonPointerFieldNames.concat(pointerFieldNames.map(name => `_p_${name}`)); const collectionUpdate = { '$unset' : {} }; @@ -124,10 +120,9 @@ export class MongoStorageAdapter { schemaUpdate['$unset'][name] = null; }); - return adaptiveCollection.updateMany({}, collectionUpdate) - .then(updateResult => { - return this.schemaCollection(collectionPrefix) - }) + return this.adaptiveCollection(className) + .then(collection => collection.updateMany({}, collectionUpdate)) + .then(updateResult => this.schemaCollection()) .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate)); } } diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index ad1347d9c0..de79f28d9d 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -10,13 +10,9 @@ var Schema = require('./../Schema'); var transform = require('./../transform'); const deepcopy = require('deepcopy'); -// options can contain: -// collectionPrefix: the string to put in front of every collection name. -function DatabaseController(adapter, { collectionPrefix } = {}) { +function DatabaseController(adapter) { this.adapter = adapter; - this.collectionPrefix = collectionPrefix; - // We don't want a mutable this.schema, because then you could have // one request that uses different schemas for different parts of // it. Instead, use loadSchema to get a schema. @@ -32,25 +28,21 @@ DatabaseController.prototype.connect = function() { }; DatabaseController.prototype.adaptiveCollection = function(className) { - return this.adapter.adaptiveCollection(this.collectionPrefix + className); + return this.adapter.adaptiveCollection(className); }; DatabaseController.prototype.schemaCollection = function() { - return this.adapter.schemaCollection(this.collectionPrefix); + return this.adapter.schemaCollection(); }; DatabaseController.prototype.collectionExists = function(className) { - return this.adapter.collectionExists(this.collectionPrefix + className); + return this.adapter.collectionExists(className); }; DatabaseController.prototype.dropCollection = function(className) { - return this.adapter.dropCollection(this.collectionPrefix + className); + return this.adapter.dropCollection(className); }; -function returnsTrue() { - return true; -} - DatabaseController.prototype.validateClassName = function(className) { if (!Schema.classNameIsValid(className)) { const error = new Parse.Error(Parse.Error.INVALID_CLASS_NAME, 'invalid className: ' + className); @@ -62,7 +54,7 @@ DatabaseController.prototype.validateClassName = function(className) { // Returns a promise for a schema object. // If we are provided a acceptor, then we run it on the schema. // If the schema isn't accepted, we reload it at most once. -DatabaseController.prototype.loadSchema = function(acceptor = returnsTrue) { +DatabaseController.prototype.loadSchema = function(acceptor = () => true) { if (!this.schemaPromise) { this.schemaPromise = this.schemaCollection().then(collection => { @@ -388,10 +380,8 @@ DatabaseController.prototype.mongoFind = function(className, query, options = {} DatabaseController.prototype.deleteEverything = function() { this.schemaPromise = null; - return this.adapter.collectionsContaining(this.collectionPrefix).then(collections => { - let promises = collections.map(collection => { - return collection.drop(); - }); + return this.adapter.allCollections().then(collections => { + let promises = collections.map(collection => collection.drop()); return Promise.all(promises); }); }; diff --git a/src/DatabaseAdapter.js b/src/DatabaseAdapter.js index a7482bd330..73bc09334f 100644 --- a/src/DatabaseAdapter.js +++ b/src/DatabaseAdapter.js @@ -18,17 +18,10 @@ import DatabaseController from './Controllers/DatabaseController'; import MongoStorageAdapter from './Adapters/Storage/Mongo/MongoStorageAdapter'; -const DefaultDatabaseURI = 'mongodb://localhost:27017/parse'; - let dbConnections = {}; -let databaseURI = DefaultDatabaseURI; let appDatabaseURIs = {}; let appDatabaseOptions = {}; -function setDatabaseURI(uri) { - databaseURI = uri; -} - function setAppDatabaseURI(appId, uri) { appDatabaseURIs[appId] = uri; } @@ -61,26 +54,21 @@ function getDatabaseConnection(appId: string, collectionPrefix: string) { return dbConnections[appId]; } - var dbURI = (appDatabaseURIs[appId] ? appDatabaseURIs[appId] : databaseURI); - - let storageAdapter = new MongoStorageAdapter({ - uri: dbURI, + let mongoAdapterOptions = { collectionPrefix: collectionPrefix, - mongoOptions: appDatabaseOptions[appId] - }); + mongoOptions: appDatabaseOptions[appId], + uri: appDatabaseURIs[appId], //may be undefined if the user didn't supply a URI, in which case the default will be used + } + + dbConnections[appId] = new DatabaseController(new MongoStorageAdapter(mongoAdapterOptions)); - dbConnections[appId] = new DatabaseController(storageAdapter, { - collectionPrefix: collectionPrefix - }); return dbConnections[appId]; } module.exports = { getDatabaseConnection: getDatabaseConnection, - setDatabaseURI: setDatabaseURI, setAppDatabaseOptions: setAppDatabaseOptions, setAppDatabaseURI: setAppDatabaseURI, clearDatabaseSettings: clearDatabaseSettings, destroyAllDataPermanently: destroyAllDataPermanently, - defaultDatabaseURI: databaseURI }; diff --git a/src/ParseServer.js b/src/ParseServer.js index 80ba3332c9..e835a25146 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -53,8 +53,6 @@ addParseCloud(); // ParseServer works like a constructor of an express app. // The args that we understand are: -// "databaseAdapter": a class like DatabaseController providing create, find, -// update, and delete // "filesAdapter": a class like GridStoreAdapter providing create, get, // and delete // "loggerAdapter": a class like FileLoggerAdapter providing info, error, @@ -88,7 +86,7 @@ class ParseServer { push, loggerAdapter, logsFolder, - databaseURI = DatabaseAdapter.defaultDatabaseURI, + databaseURI, databaseOptions, cloud, collectionPrefix = '', @@ -130,9 +128,7 @@ class ParseServer { DatabaseAdapter.setAppDatabaseOptions(appId, databaseOptions); } - if (databaseURI) { - DatabaseAdapter.setAppDatabaseURI(appId, databaseURI); - } + DatabaseAdapter.setAppDatabaseURI(appId, databaseURI); if (cloud) { addParseCloud(); diff --git a/src/Schema.js b/src/Schema.js index 385b3010e5..46983b4049 100644 --- a/src/Schema.js +++ b/src/Schema.js @@ -525,9 +525,7 @@ class Schema { if (this.data[className][fieldName].type == 'Relation') { //For relations, drop the _Join table - return database.adaptiveCollection(className).then(collection => { - return database.adapter.deleteFields(className, [fieldName], [], database.collectionPrefix, collection); - }) + return database.adapter.deleteFields(className, [fieldName], []) .then(() => database.dropCollection(`_Join:${fieldName}:${className}`)) .catch(error => { // 'ns not found' means collection was already gone. Ignore deletion attempt. @@ -541,8 +539,7 @@ class Schema { const fieldNames = [fieldName]; const pointerFieldNames = this.data[className][fieldName].type === 'Pointer' ? [fieldName] : []; - return database.adaptiveCollection(className) - .then(collection => database.adapter.deleteFields(className, fieldNames, pointerFieldNames, database.collectionPrefix, collection)); + return database.adapter.deleteFields(className, fieldNames, pointerFieldNames); }); } diff --git a/src/testing-routes.js b/src/testing-routes.js index 20173fe38f..04b5cf83ed 100644 --- a/src/testing-routes.js +++ b/src/testing-routes.js @@ -1,8 +1,8 @@ // testing-routes.js -import cache from './cache'; +import cache from './cache'; import * as middlewares from './middlewares'; -import { ParseServer } from './index'; -import { Parse } from 'parse/node'; +import { ParseServer } from './index'; +import { Parse } from 'parse/node'; var express = require('express'), cryptoUtils = require('./cryptoUtils'); @@ -31,7 +31,7 @@ function createApp(req, res) { res.status(200).send(keys); } -// deletes all collections with the collectionPrefix of the app +// deletes all collections that belong to the app function clearApp(req, res) { if (!req.auth.isMaster) { return res.status(401).send({ "error": "unauthorized" });