Merge pull request #698 from ParsePlatform/nlutsenko.database.controller

Rename ExportAdapter to DatabaseController, start splitting Mongo specific logic.
This commit is contained in:
Nikita Lutsenko
2016-02-29 14:09:03 -08:00
12 changed files with 123 additions and 91 deletions

View File

@@ -0,0 +1,17 @@
'use strict';
let DatabaseController = require('../src/Controllers/DatabaseController');
let MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');
describe('DatabaseController', () => {
it('can be constructed', done => {
let adapter = new MongoStorageAdapter('mongodb://localhost:27017/test');
let databaseController = new DatabaseController(adapter, {
collectionPrefix: 'test_'
});
databaseController.connect().then(done, error => {
console.log('error', error.stack);
fail();
});
});
});

View File

@@ -1,15 +0,0 @@
var ExportAdapter = require('../src/ExportAdapter');
describe('ExportAdapter', () => {
it('can be constructed', (done) => {
var database = new ExportAdapter('mongodb://localhost:27017/test',
{
collectionPrefix: 'test_'
});
database.connect().then(done, (error) => {
console.log('error', error.stack);
fail();
});
});
});

View File

@@ -520,11 +520,11 @@ describe('Schema', () => {
return obj2.save(); return obj2.save();
}) })
.then(() => { .then(() => {
config.database.db.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => { config.database.adapter.database.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => {
expect(err).toEqual(null); expect(err).toEqual(null);
config.database.loadSchema() config.database.loadSchema()
.then(schema => schema.deleteField('aRelation', 'HasPointersAndRelations', config.database.db, 'test_')) .then(schema => schema.deleteField('aRelation', 'HasPointersAndRelations', config.database.adapter.database, 'test_'))
.then(() => config.database.db.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => { .then(() => config.database.adapter.database.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => {
expect(err).not.toEqual(null); expect(err).not.toEqual(null);
done(); done();
})) }))
@@ -538,7 +538,7 @@ describe('Schema', () => {
var obj2 = hasAllPODobject(); var obj2 = hasAllPODobject();
var p = Parse.Object.saveAll([obj1, obj2]) var p = Parse.Object.saveAll([obj1, obj2])
.then(() => config.database.loadSchema()) .then(() => config.database.loadSchema())
.then(schema => schema.deleteField('aString', 'HasAllPOD', config.database.db, 'test_')) .then(schema => schema.deleteField('aString', 'HasAllPOD', config.database.adapter.database, 'test_'))
.then(() => new Parse.Query('HasAllPOD').get(obj1.id)) .then(() => new Parse.Query('HasAllPOD').get(obj1.id))
.then(obj1Reloaded => { .then(obj1Reloaded => {
expect(obj1Reloaded.get('aString')).toEqual(undefined); expect(obj1Reloaded.get('aString')).toEqual(undefined);
@@ -568,7 +568,7 @@ describe('Schema', () => {
expect(obj1.get('aPointer').id).toEqual(obj1.id); expect(obj1.get('aPointer').id).toEqual(obj1.id);
}) })
.then(() => config.database.loadSchema()) .then(() => config.database.loadSchema())
.then(schema => schema.deleteField('aPointer', 'NewClass', config.database.db, 'test_')) .then(schema => schema.deleteField('aPointer', 'NewClass', config.database.adapter.database, 'test_'))
.then(() => new Parse.Query('NewClass').get(obj1.id)) .then(() => new Parse.Query('NewClass').get(obj1.id))
.then(obj1 => { .then(obj1 => {
expect(obj1.get('aPointer')).toEqual(undefined); expect(obj1.get('aPointer')).toEqual(undefined);

View File

@@ -710,10 +710,10 @@ describe('schemas', () => {
}, (error, response, body) => { }, (error, response, body) => {
expect(response.statusCode).toEqual(200); expect(response.statusCode).toEqual(200);
expect(response.body).toEqual({}); expect(response.body).toEqual({});
config.database.db.collection('test__Join:aRelation:MyOtherClass', { strict: true }, (err, coll) => { config.database.adapter.database.collection('test__Join:aRelation:MyOtherClass', { strict: true }, (err, coll) => {
//Expect Join table to be gone //Expect Join table to be gone
expect(err).not.toEqual(null); expect(err).not.toEqual(null);
config.database.db.collection('test_MyOtherClass', { strict: true }, (err, coll) => { config.database.adapter.database.collection('test_MyOtherClass', { strict: true }, (err, coll) => {
// Expect data table to be gone // Expect data table to be gone
expect(err).not.toEqual(null); expect(err).not.toEqual(null);
request.get({ request.get({

View File

@@ -8,7 +8,7 @@
// * getFileLocation(config, request, filename) // * getFileLocation(config, request, filename)
// //
// Default is GridStoreAdapter, which requires mongo // Default is GridStoreAdapter, which requires mongo
// and for the API server to be using the ExportAdapter // and for the API server to be using the DatabaseController with Mongo
// database adapter. // database adapter.
export class FilesAdapter { export class FilesAdapter {

View File

@@ -11,7 +11,7 @@ export class GridStoreAdapter extends FilesAdapter {
// Returns a promise // Returns a promise
createFile(config, filename, data) { createFile(config, filename, data) {
return config.database.connect().then(() => { return config.database.connect().then(() => {
let gridStore = new GridStore(config.database.db, filename, 'w'); let gridStore = new GridStore(config.database.adapter.database, filename, 'w');
return gridStore.open(); return gridStore.open();
}).then((gridStore) => { }).then((gridStore) => {
return gridStore.write(data); return gridStore.write(data);
@@ -22,7 +22,7 @@ export class GridStoreAdapter extends FilesAdapter {
deleteFile(config, filename) { deleteFile(config, filename) {
return config.database.connect().then(() => { return config.database.connect().then(() => {
let gridStore = new GridStore(config.database.db, filename, 'w'); let gridStore = new GridStore(config.database.adapter.database, filename, 'w');
return gridStore.open(); return gridStore.open();
}).then((gridStore) => { }).then((gridStore) => {
return gridStore.unlink(); return gridStore.unlink();
@@ -33,9 +33,9 @@ export class GridStoreAdapter extends FilesAdapter {
getFileData(config, filename) { getFileData(config, filename) {
return config.database.connect().then(() => { return config.database.connect().then(() => {
return GridStore.exist(config.database.db, filename); return GridStore.exist(config.database.adapter.database, filename);
}).then(() => { }).then(() => {
let gridStore = new GridStore(config.database.db, filename, 'r'); let gridStore = new GridStore(config.database.adapter.database, filename, 'r');
return gridStore.open(); return gridStore.open();
}).then((gridStore) => { }).then((gridStore) => {
return gridStore.read(); return gridStore.read();

View File

@@ -0,0 +1,49 @@
let mongodb = require('mongodb');
let MongoClient = mongodb.MongoClient;
export class MongoStorageAdapter {
// Private
_uri: string;
// Public
connectionPromise;
database;
constructor(uri: string) {
this._uri = uri;
}
connect() {
if (this.connectionPromise) {
return this.connectionPromise;
}
this.connectionPromise = MongoClient.connect(this._uri).then(database => {
this.database = database;
});
return this.connectionPromise;
}
collection(name: string) {
return this.connect().then(() => {
return this.database.collection(name);
});
}
// Used for testing only right now.
collectionsContaining(match: string) {
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);
});
});
}
}
export default MongoStorageAdapter;
module.exports = MongoStorageAdapter; // Required for tests

View File

@@ -2,18 +2,17 @@
// Parse database. // Parse database.
var mongodb = require('mongodb'); var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var Parse = require('parse/node').Parse; var Parse = require('parse/node').Parse;
var Schema = require('./Schema'); var Schema = require('./../Schema');
var transform = require('./transform'); var transform = require('./../transform');
// options can contain: // options can contain:
// collectionPrefix: the string to put in front of every collection name. // collectionPrefix: the string to put in front of every collection name.
function ExportAdapter(mongoURI, options = {}) { function DatabaseController(adapter, { collectionPrefix } = {}) {
this.mongoURI = mongoURI; this.adapter = adapter;
this.collectionPrefix = options.collectionPrefix; this.collectionPrefix = collectionPrefix;
// We don't want a mutable this.schema, because then you could have // We don't want a mutable this.schema, because then you could have
// one request that uses different schemas for different parts of // one request that uses different schemas for different parts of
@@ -25,25 +24,13 @@ function ExportAdapter(mongoURI, options = {}) {
// Connects to the database. Returns a promise that resolves when the // Connects to the database. Returns a promise that resolves when the
// connection is successful. // connection is successful.
// this.db will be populated with a Mongo "Db" object when the DatabaseController.prototype.connect = function() {
// promise resolves successfully. return this.adapter.connect();
ExportAdapter.prototype.connect = function() {
if (this.connectionPromise) {
// There's already a connection in progress.
return this.connectionPromise;
}
this.connectionPromise = Promise.resolve().then(() => {
return MongoClient.connect(this.mongoURI);
}).then((db) => {
this.db = db;
});
return this.connectionPromise;
}; };
// Returns a promise for a Mongo collection. // Returns a promise for a Mongo collection.
// Generally just for internal use. // Generally just for internal use.
ExportAdapter.prototype.collection = function(className) { DatabaseController.prototype.collection = function(className) {
if (!Schema.classNameIsValid(className)) { if (!Schema.classNameIsValid(className)) {
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME,
'invalid className: ' + className); 'invalid className: ' + className);
@@ -51,10 +38,8 @@ ExportAdapter.prototype.collection = function(className) {
return this.rawCollection(className); return this.rawCollection(className);
}; };
ExportAdapter.prototype.rawCollection = function(className) { DatabaseController.prototype.rawCollection = function(className) {
return this.connect().then(() => { return this.adapter.collection(this.collectionPrefix + className);
return this.db.collection(this.collectionPrefix + className);
});
}; };
function returnsTrue() { function returnsTrue() {
@@ -64,7 +49,7 @@ function returnsTrue() {
// Returns a promise for a schema object. // Returns a promise for a schema object.
// If we are provided a acceptor, then we run it on the schema. // 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. // If the schema isn't accepted, we reload it at most once.
ExportAdapter.prototype.loadSchema = function(acceptor = returnsTrue) { DatabaseController.prototype.loadSchema = function(acceptor = returnsTrue) {
if (!this.schemaPromise) { if (!this.schemaPromise) {
this.schemaPromise = this.collection('_SCHEMA').then((coll) => { this.schemaPromise = this.collection('_SCHEMA').then((coll) => {
@@ -88,8 +73,8 @@ ExportAdapter.prototype.loadSchema = function(acceptor = returnsTrue) {
// Returns a promise for the classname that is related to the given // Returns a promise for the classname that is related to the given
// classname through the key. // classname through the key.
// TODO: make this not in the ExportAdapter interface // TODO: make this not in the DatabaseController interface
ExportAdapter.prototype.redirectClassNameForKey = function(className, key) { DatabaseController.prototype.redirectClassNameForKey = function(className, key) {
return this.loadSchema().then((schema) => { return this.loadSchema().then((schema) => {
var t = schema.getExpectedType(className, key); var t = schema.getExpectedType(className, key);
var match = t.match(/^relation<(.*)>$/); var match = t.match(/^relation<(.*)>$/);
@@ -105,7 +90,7 @@ ExportAdapter.prototype.redirectClassNameForKey = function(className, key) {
// Returns a promise that resolves to the new schema. // Returns a promise that resolves to the new schema.
// This does not update this.schema, because in a situation like a // This does not update this.schema, because in a situation like a
// batch request, that could confuse other users of the schema. // batch request, that could confuse other users of the schema.
ExportAdapter.prototype.validateObject = function(className, object, query) { DatabaseController.prototype.validateObject = function(className, object, query) {
return this.loadSchema().then((schema) => { return this.loadSchema().then((schema) => {
return schema.validateObject(className, object, query); return schema.validateObject(className, object, query);
}); });
@@ -113,7 +98,7 @@ ExportAdapter.prototype.validateObject = function(className, object, query) {
// Like transform.untransformObject but you need to provide a className. // Like transform.untransformObject but you need to provide a className.
// Filters out any data that shouldn't be on this REST-formatted object. // Filters out any data that shouldn't be on this REST-formatted object.
ExportAdapter.prototype.untransformObject = function( DatabaseController.prototype.untransformObject = function(
schema, isMaster, aclGroup, className, mongoObject) { schema, isMaster, aclGroup, className, mongoObject) {
var object = transform.untransformObject(schema, className, mongoObject); var object = transform.untransformObject(schema, className, mongoObject);
@@ -138,7 +123,7 @@ ExportAdapter.prototype.untransformObject = function(
// acl: a list of strings. If the object to be updated has an ACL, // acl: a list of strings. If the object to be updated has an ACL,
// one of the provided strings must provide the caller with // one of the provided strings must provide the caller with
// write permissions. // write permissions.
ExportAdapter.prototype.update = function(className, query, update, options) { DatabaseController.prototype.update = function(className, query, update, options) {
var acceptor = function(schema) { var acceptor = function(schema) {
return schema.hasKeys(className, Object.keys(query)); return schema.hasKeys(className, Object.keys(query));
}; };
@@ -196,7 +181,7 @@ ExportAdapter.prototype.update = function(className, query, update, options) {
// Returns a promise that resolves successfully when these are // Returns a promise that resolves successfully when these are
// processed. // processed.
// This mutates update. // This mutates update.
ExportAdapter.prototype.handleRelationUpdates = function(className, DatabaseController.prototype.handleRelationUpdates = function(className,
objectId, objectId,
update) { update) {
var pending = []; var pending = [];
@@ -243,7 +228,7 @@ ExportAdapter.prototype.handleRelationUpdates = function(className,
// Adds a relation. // Adds a relation.
// Returns a promise that resolves successfully iff the add was successful. // Returns a promise that resolves successfully iff the add was successful.
ExportAdapter.prototype.addRelation = function(key, fromClassName, DatabaseController.prototype.addRelation = function(key, fromClassName,
fromId, toId) { fromId, toId) {
var doc = { var doc = {
relatedId: toId, relatedId: toId,
@@ -258,7 +243,7 @@ ExportAdapter.prototype.addRelation = function(key, fromClassName,
// Removes a relation. // Removes a relation.
// Returns a promise that resolves successfully iff the remove was // Returns a promise that resolves successfully iff the remove was
// successful. // successful.
ExportAdapter.prototype.removeRelation = function(key, fromClassName, DatabaseController.prototype.removeRelation = function(key, fromClassName,
fromId, toId) { fromId, toId) {
var doc = { var doc = {
relatedId: toId, relatedId: toId,
@@ -277,7 +262,7 @@ ExportAdapter.prototype.removeRelation = function(key, fromClassName,
// acl: a list of strings. If the object to be updated has an ACL, // acl: a list of strings. If the object to be updated has an ACL,
// one of the provided strings must provide the caller with // one of the provided strings must provide the caller with
// write permissions. // write permissions.
ExportAdapter.prototype.destroy = function(className, query, options = {}) { DatabaseController.prototype.destroy = function(className, query, options = {}) {
var isMaster = !('acl' in options); var isMaster = !('acl' in options);
var aclGroup = options.acl || []; var aclGroup = options.acl || [];
@@ -320,7 +305,7 @@ ExportAdapter.prototype.destroy = function(className, query, options = {}) {
// Inserts an object into the database. // Inserts an object into the database.
// Returns a promise that resolves successfully iff the object saved. // Returns a promise that resolves successfully iff the object saved.
ExportAdapter.prototype.create = function(className, object, options) { DatabaseController.prototype.create = function(className, object, options) {
var schema; var schema;
var isMaster = !('acl' in options); var isMaster = !('acl' in options);
var aclGroup = options.acl || []; var aclGroup = options.acl || [];
@@ -346,7 +331,7 @@ ExportAdapter.prototype.create = function(className, object, options) {
// This should only be used for testing - use 'find' for normal code // This should only be used for testing - use 'find' for normal code
// to avoid Mongo-format dependencies. // to avoid Mongo-format dependencies.
// Returns a promise that resolves to a list of items. // Returns a promise that resolves to a list of items.
ExportAdapter.prototype.mongoFind = function(className, query, options = {}) { DatabaseController.prototype.mongoFind = function(className, query, options = {}) {
return this.collection(className).then((coll) => { return this.collection(className).then((coll) => {
return coll.find(query, options).toArray(); return coll.find(query, options).toArray();
}); });
@@ -355,19 +340,13 @@ ExportAdapter.prototype.mongoFind = function(className, query, options = {}) {
// Deletes everything in the database matching the current collectionPrefix // Deletes everything in the database matching the current collectionPrefix
// Won't delete collections in the system namespace // Won't delete collections in the system namespace
// Returns a promise. // Returns a promise.
ExportAdapter.prototype.deleteEverything = function() { DatabaseController.prototype.deleteEverything = function() {
this.schemaPromise = null; this.schemaPromise = null;
return this.connect().then(() => { return this.adapter.collectionsContaining(this.collectionPrefix).then(collections => {
return this.db.collections(); let promises = collections.map(collection => {
}).then((colls) => { return collection.drop();
var promises = []; });
for (var coll of colls) {
if (!coll.namespace.match(/\.system\./) &&
coll.collectionName.indexOf(this.collectionPrefix) === 0) {
promises.push(coll.drop());
}
}
return Promise.all(promises); return Promise.all(promises);
}); });
}; };
@@ -390,7 +369,7 @@ function keysForQuery(query) {
// Returns a promise for a list of related ids given an owning id. // Returns a promise for a list of related ids given an owning id.
// className here is the owning className. // className here is the owning className.
ExportAdapter.prototype.relatedIds = function(className, key, owningId) { DatabaseController.prototype.relatedIds = function(className, key, owningId) {
var joinTable = '_Join:' + key + ':' + className; var joinTable = '_Join:' + key + ':' + className;
return this.collection(joinTable).then((coll) => { return this.collection(joinTable).then((coll) => {
return coll.find({owningId: owningId}).toArray(); return coll.find({owningId: owningId}).toArray();
@@ -401,7 +380,7 @@ ExportAdapter.prototype.relatedIds = function(className, key, owningId) {
// Returns a promise for a list of owning ids given some related ids. // Returns a promise for a list of owning ids given some related ids.
// className here is the owning className. // className here is the owning className.
ExportAdapter.prototype.owningIds = function(className, key, relatedIds) { DatabaseController.prototype.owningIds = function(className, key, relatedIds) {
var joinTable = '_Join:' + key + ':' + className; var joinTable = '_Join:' + key + ':' + className;
return this.collection(joinTable).then((coll) => { return this.collection(joinTable).then((coll) => {
return coll.find({relatedId: {'$in': relatedIds}}).toArray(); return coll.find({relatedId: {'$in': relatedIds}}).toArray();
@@ -414,7 +393,7 @@ ExportAdapter.prototype.owningIds = function(className, key, relatedIds) {
// equal-to-pointer constraints on relation fields. // equal-to-pointer constraints on relation fields.
// Returns a promise that resolves when query is mutated // Returns a promise that resolves when query is mutated
// TODO: this only handles one of these at a time - make it handle more // TODO: this only handles one of these at a time - make it handle more
ExportAdapter.prototype.reduceInRelation = function(className, query, schema) { DatabaseController.prototype.reduceInRelation = function(className, query, schema) {
// Search for an in-relation or equal-to-relation // Search for an in-relation or equal-to-relation
for (var key in query) { for (var key in query) {
if (query[key] && if (query[key] &&
@@ -442,7 +421,7 @@ ExportAdapter.prototype.reduceInRelation = function(className, query, schema) {
// Modifies query so that it no longer has $relatedTo // Modifies query so that it no longer has $relatedTo
// Returns a promise that resolves when query is mutated // Returns a promise that resolves when query is mutated
ExportAdapter.prototype.reduceRelationKeys = function(className, query) { DatabaseController.prototype.reduceRelationKeys = function(className, query) {
var relatedTo = query['$relatedTo']; var relatedTo = query['$relatedTo'];
if (relatedTo) { if (relatedTo) {
return this.relatedIds( return this.relatedIds(
@@ -461,7 +440,7 @@ ExportAdapter.prototype.reduceRelationKeys = function(className, query) {
// none, then build the geoindex. // none, then build the geoindex.
// This could be improved a lot but it's not clear if that's a good // This could be improved a lot but it's not clear if that's a good
// idea. Or even if this behavior is a good idea. // idea. Or even if this behavior is a good idea.
ExportAdapter.prototype.smartFind = function(coll, where, options) { DatabaseController.prototype.smartFind = function(coll, where, options) {
return coll.find(where, options).toArray() return coll.find(where, options).toArray()
.then((result) => { .then((result) => {
return result; return result;
@@ -502,7 +481,7 @@ ExportAdapter.prototype.smartFind = function(coll, where, options) {
// TODO: make userIds not needed here. The db adapter shouldn't know // TODO: make userIds not needed here. The db adapter shouldn't know
// anything about users, ideally. Then, improve the format of the ACL // anything about users, ideally. Then, improve the format of the ACL
// arg to work like the others. // arg to work like the others.
ExportAdapter.prototype.find = function(className, query, options = {}) { DatabaseController.prototype.find = function(className, query, options = {}) {
var mongoOptions = {}; var mongoOptions = {};
if (options.skip) { if (options.skip) {
mongoOptions.skip = options.skip; mongoOptions.skip = options.skip;
@@ -568,4 +547,4 @@ ExportAdapter.prototype.find = function(className, query, options = {}) {
}); });
}; };
module.exports = ExportAdapter; module.exports = DatabaseController;

View File

@@ -13,11 +13,12 @@
// * destroy(className, query, options) // * destroy(className, query, options)
// * This list is incomplete and the database process is not fully modularized. // * This list is incomplete and the database process is not fully modularized.
// //
// Default is ExportAdapter, which uses mongo. // Default is MongoStorageAdapter.
var ExportAdapter = require('./ExportAdapter'); import DatabaseController from './Controllers/DatabaseController';
import MongoStorageAdapter from './Adapters/Storage/Mongo/MongoStorageAdapter';
var adapter = ExportAdapter; let adapter = MongoStorageAdapter;
var dbConnections = {}; var dbConnections = {};
var databaseURI = 'mongodb://localhost:27017/parse'; var databaseURI = 'mongodb://localhost:27017/parse';
var appDatabaseURIs = {}; var appDatabaseURIs = {};
@@ -46,10 +47,11 @@ function getDatabaseConnection(appId: string, collectionPrefix: string) {
} }
var dbURI = (appDatabaseURIs[appId] ? appDatabaseURIs[appId] : databaseURI); var dbURI = (appDatabaseURIs[appId] ? appDatabaseURIs[appId] : databaseURI);
dbConnections[appId] = new adapter(dbURI, {
let storageAdapter = new adapter(dbURI);
dbConnections[appId] = new DatabaseController(storageAdapter, {
collectionPrefix: collectionPrefix collectionPrefix: collectionPrefix
}); });
dbConnections[appId].connect();
return dbConnections[appId]; return dbConnections[appId];
} }

View File

@@ -164,7 +164,7 @@ function modifySchema(req) {
.then(() => schema.deleteField( .then(() => schema.deleteField(
submittedFieldName, submittedFieldName,
className, className,
req.config.database.db, req.config.database.adapter.database,
req.config.database.collectionPrefix req.config.database.collectionPrefix
)); ));
deletionPromises.push(promise); deletionPromises.push(promise);
@@ -246,7 +246,7 @@ function deleteSchema(req) {
//tried to delete non-existant class //tried to delete non-existant class
resolve({ response: {}}); resolve({ response: {}});
} else { } else {
removeJoinTables(req.config.database.db, req.config.database.collectionPrefix, doc.value) removeJoinTables(req.config.database.adapter.database, req.config.database.collectionPrefix, doc.value)
.then(resolve, reject); .then(resolve, reject);
} }
}); });

View File

@@ -10,7 +10,7 @@
// keeping it this way for now. // keeping it this way for now.
// //
// In API-handling code, you should only use the Schema class via the // In API-handling code, you should only use the Schema class via the
// ExportAdapter. This will let us replace the schema logic for // DatabaseController. This will let us replace the schema logic for
// different databases. // different databases.
// TODO: hide all schema logic inside the database adapter. // TODO: hide all schema logic inside the database adapter.

View File

@@ -45,7 +45,7 @@ addParseCloud();
// ParseServer works like a constructor of an express app. // ParseServer works like a constructor of an express app.
// The args that we understand are: // The args that we understand are:
// "databaseAdapter": a class like ExportAdapter providing create, find, // "databaseAdapter": a class like DatabaseController providing create, find,
// update, and delete // update, and delete
// "filesAdapter": a class like GridStoreAdapter providing create, get, // "filesAdapter": a class like GridStoreAdapter providing create, get,
// and delete // and delete