Skip to content

Remove collection prefix and default mongo URI #1479

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
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
7 changes: 3 additions & 4 deletions spec/DatabaseController.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
6 changes: 4 additions & 2 deletions src/Adapters/Files/GridStoreAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<Db>;

constructor(mongoDatabaseURI: string) {
constructor(mongoDatabaseURI = DefaultMongoURI) {
super();
this._databaseURI = mongoDatabaseURI;
this._connect();
Expand Down
33 changes: 14 additions & 19 deletions src/Adapters/Storage/Mongo/MongoStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -18,7 +19,7 @@ export class MongoStorageAdapter {
database;

constructor({
uri,
uri = DefaultMongoURI,
collectionPrefix = '',
mongoOptions = {},
}) {
Expand Down Expand Up @@ -50,37 +51,38 @@ 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 => {
return collections.filter(collection => {
if (collection.namespace.match(/\.system\./)) {
return false;
}
return (collection.collectionName.indexOf(match) == 0);
return (collection.collectionName.indexOf(this._collectionPrefix) == 0);
});
});
}
Expand All @@ -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' : {} };
Expand All @@ -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));
}
}
Expand Down
26 changes: 8 additions & 18 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
Expand All @@ -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 => {
Expand Down Expand Up @@ -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);
});
};
Expand Down
24 changes: 6 additions & 18 deletions src/DatabaseAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
};
8 changes: 2 additions & 6 deletions src/ParseServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -88,7 +86,7 @@ class ParseServer {
push,
loggerAdapter,
logsFolder,
databaseURI = DatabaseAdapter.defaultDatabaseURI,
databaseURI,
databaseOptions,
cloud,
collectionPrefix = '',
Expand Down Expand Up @@ -130,9 +128,7 @@ class ParseServer {
DatabaseAdapter.setAppDatabaseOptions(appId, databaseOptions);
}

if (databaseURI) {
DatabaseAdapter.setAppDatabaseURI(appId, databaseURI);
}
DatabaseAdapter.setAppDatabaseURI(appId, databaseURI);

if (cloud) {
addParseCloud();
Expand Down
7 changes: 2 additions & 5 deletions src/Schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -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], [])
Copy link
Contributor

Choose a reason for hiding this comment

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

can we expose the deleteFields on the dbController just so we don't call adapter here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't really see the point of having a super thin extra dbController layer that just calls into some other function.

Copy link
Contributor

Choose a reason for hiding this comment

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

Just to hide the internals as they are pretty much hidden everywhere else. but that's ok

.then(() => database.dropCollection(`_Join:${fieldName}:${className}`))
.catch(error => {
// 'ns not found' means collection was already gone. Ignore deletion attempt.
Expand All @@ -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);
});
}

Expand Down
8 changes: 4 additions & 4 deletions src/testing-routes.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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" });
Expand Down