Skip to content

Schema.js database agnostic #1468

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 2 commits into from
Apr 12, 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
29 changes: 15 additions & 14 deletions spec/Schema.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -685,9 +685,10 @@ describe('Schema', () => {
.then(() => schema.reloadData())
.then(() => {
expect(schema['data']['NewClass']).toEqual({
objectId: 'string',
updatedAt: 'string',
createdAt: 'string'
objectId: { type: 'String' },
updatedAt: { type: 'Date' },
createdAt: { type: 'Date' },
ACL: { type: 'ACL' }
});
done();
});
Expand Down Expand Up @@ -747,7 +748,7 @@ describe('Schema', () => {
it('can merge schemas', done => {
expect(Schema.buildMergedSchemaObject({
_id: 'SomeClass',
someType: 'number'
someType: { type: 'Number' }
}, {
newType: {type: 'Number'}
})).toEqual({
Expand All @@ -760,8 +761,8 @@ describe('Schema', () => {
it('can merge deletions', done => {
expect(Schema.buildMergedSchemaObject({
_id: 'SomeClass',
someType: 'number',
outDatedType: 'string',
someType: { type: 'Number' },
outDatedType: { type: 'String' },
},{
newType: {type: 'GeoPoint'},
outDatedType: {__op: 'Delete'},
Expand All @@ -775,16 +776,16 @@ describe('Schema', () => {
it('ignore default field when merge with system class', done => {
expect(Schema.buildMergedSchemaObject({
_id: '_User',
username: 'string',
password: 'string',
authData: 'object',
email: 'string',
emailVerified: 'boolean'
username: { type: 'String' },
password: { type: 'String' },
authData: { type: 'Object' },
email: { type: 'String' },
emailVerified: { type: 'Boolean' },
},{
authData: {type: 'string'},
customField: {type: 'string'},
authData: { type: 'String' },
customField: { type: 'String' },
})).toEqual({
customField: {type: 'string'}
customField: { type: 'String' }
});
done();
});
Expand Down
6 changes: 3 additions & 3 deletions spec/transform.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ var dummySchema = {
data: {},
getExpectedType: function(className, key) {
if (key == 'userPointer') {
return '*_User';
return { type: 'Pointer', targetClass: '_User' };
} else if (key == 'picture') {
return 'file';
return { type: 'File' };
} else if (key == 'location') {
return 'geopoint';
return { type: 'GeoPoint' };
}
return;
},
Expand Down
57 changes: 47 additions & 10 deletions src/Adapters/Storage/Mongo/MongoSchemaCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,33 @@ function parseFieldTypeToMongoFieldType({ type, targetClass }) {
}
}

// Returns { code, error } if invalid, or { result }, an object
// suitable for inserting into _SCHEMA collection, otherwise.
function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPermissions) {

let mongoObject = {
_id: className,
objectId: 'string',
updatedAt: 'string',
createdAt: 'string'
};

for (let fieldName in fields) {
mongoObject[fieldName] = parseFieldTypeToMongoFieldType(fields[fieldName]);
}

if (typeof classLevelPermissions !== 'undefined') {
mongoObject._metadata = mongoObject._metadata || {};
if (!classLevelPermissions) {
delete mongoObject._metadata.class_permissions;
} else {
mongoObject._metadata.class_permissions = classLevelPermissions;
}
}

return mongoObject;
}

class MongoSchemaCollection {
_collection: MongoCollection;

Expand Down Expand Up @@ -136,8 +163,9 @@ class MongoSchemaCollection {
// later PR. Returns a promise that is expected to resolve with the newly created schema, in Parse format.
// If the class already exists, returns a promise that rejects with undefined as the reason. If the collection
// can't be added for a reason other than it already existing, requirements for rejection reason are TBD.
addSchema(name: string, fields) {
let mongoObject = _mongoSchemaObjectFromNameFields(name, fields);
addSchema(name: string, fields, classLevelPermissions) {
let mongoSchema = mongoSchemaFromFieldsAndClassNameAndCLP(fields, name, classLevelPermissions);
let mongoObject = _mongoSchemaObjectFromNameFields(name, mongoSchema);
return this._collection.insertOne(mongoObject)
.then(result => mongoSchemaToParseSchema(result.ops[0]))
.catch(error => {
Expand All @@ -155,18 +183,27 @@ class MongoSchemaCollection {
upsertSchema(name: string, query: string, update) {
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
}

updateField(className: string, fieldName: string, type: string) {
// We don't have this field. Update the schema.
// Note that we use the $exists guard and $set to avoid race
// conditions in the database. This is important!
let query = {};
query[fieldName] = { '$exists': false };
let update = {};
if (typeof type === 'string') {
type = {
type: type
}
}
update[fieldName] = parseFieldTypeToMongoFieldType(type);
update = {'$set': update};
return this.upsertSchema(className, query, update);
}
}

// Exported for testing reasons and because we haven't moved all mongo schema format
// related logic into the database adapter yet.
MongoSchemaCollection._TESTmongoSchemaToParseSchema = mongoSchemaToParseSchema

// Exported because we haven't moved all mongo schema format related logic
// into the database adapter yet. We will remove this before too long.
MongoSchemaCollection._DONOTUSEmongoFieldToParseSchemaField = mongoFieldToParseSchemaField

// Exported because we haven't moved all mongo schema format related logic
// into the database adapter yet. We will remove this before too long.
MongoSchemaCollection._DONOTUSEparseFieldTypeToMongoFieldType = parseFieldTypeToMongoFieldType;

export default MongoSchemaCollection
11 changes: 4 additions & 7 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,8 @@ DatabaseController.prototype.loadSchema = function(acceptor = returnsTrue) {
DatabaseController.prototype.redirectClassNameForKey = function(className, key) {
return this.loadSchema().then((schema) => {
var t = schema.getExpectedType(className, key);
var match = t ? t.match(/^relation<(.*)>$/) : false;
if (match) {
return match[1];
if (t.type == 'Relation') {
return t.targetClass;
Copy link
Contributor

Choose a reason for hiding this comment

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

nice

} else {
return className;
}
Expand Down Expand Up @@ -446,11 +445,10 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem
let promises = Object.keys(query).map((key) => {
if (query[key] && (query[key]['$in'] || query[key]['$ne'] || query[key]['$nin'] || query[key].__type == 'Pointer')) {
let t = schema.getExpectedType(className, key);
let match = t ? t.match(/^relation<(.*)>$/) : false;
if (!match) {
if (!t || t.type !== 'Relation') {
return Promise.resolve(query);
}
let relatedClassName = match[1];
let relatedClassName = t.targetClass;
// Build the list of queries
let queries = Object.keys(query[key]).map((constraintKey) => {
let relatedIds;
Expand Down Expand Up @@ -599,7 +597,6 @@ DatabaseController.prototype.find = function(className, query, options = {}) {
if (options.limit) {
mongoOptions.limit = options.limit;
}

let isMaster = !('acl' in options);
let aclGroup = options.acl || [];
let acceptor = schema => schema.hasKeys(className, keysForQuery(query))
Expand Down
Loading