Skip to content

Commit c050a65

Browse files
committed
Schema.js database agnostic (#1468)
* Schema.js database agnostic * nits
1 parent c419106 commit c050a65

File tree

7 files changed

+177
-163
lines changed

7 files changed

+177
-163
lines changed

spec/Schema.spec.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -685,9 +685,10 @@ describe('Schema', () => {
685685
.then(() => schema.reloadData())
686686
.then(() => {
687687
expect(schema['data']['NewClass']).toEqual({
688-
objectId: 'string',
689-
updatedAt: 'string',
690-
createdAt: 'string'
688+
objectId: { type: 'String' },
689+
updatedAt: { type: 'Date' },
690+
createdAt: { type: 'Date' },
691+
ACL: { type: 'ACL' }
691692
});
692693
done();
693694
});
@@ -747,7 +748,7 @@ describe('Schema', () => {
747748
it('can merge schemas', done => {
748749
expect(Schema.buildMergedSchemaObject({
749750
_id: 'SomeClass',
750-
someType: 'number'
751+
someType: { type: 'Number' }
751752
}, {
752753
newType: {type: 'Number'}
753754
})).toEqual({
@@ -760,8 +761,8 @@ describe('Schema', () => {
760761
it('can merge deletions', done => {
761762
expect(Schema.buildMergedSchemaObject({
762763
_id: 'SomeClass',
763-
someType: 'number',
764-
outDatedType: 'string',
764+
someType: { type: 'Number' },
765+
outDatedType: { type: 'String' },
765766
},{
766767
newType: {type: 'GeoPoint'},
767768
outDatedType: {__op: 'Delete'},
@@ -775,16 +776,16 @@ describe('Schema', () => {
775776
it('ignore default field when merge with system class', done => {
776777
expect(Schema.buildMergedSchemaObject({
777778
_id: '_User',
778-
username: 'string',
779-
password: 'string',
780-
authData: 'object',
781-
email: 'string',
782-
emailVerified: 'boolean'
779+
username: { type: 'String' },
780+
password: { type: 'String' },
781+
authData: { type: 'Object' },
782+
email: { type: 'String' },
783+
emailVerified: { type: 'Boolean' },
783784
},{
784-
authData: {type: 'string'},
785-
customField: {type: 'string'},
785+
authData: { type: 'String' },
786+
customField: { type: 'String' },
786787
})).toEqual({
787-
customField: {type: 'string'}
788+
customField: { type: 'String' }
788789
});
789790
done();
790791
});

spec/transform.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ var dummySchema = {
88
data: {},
99
getExpectedType: function(className, key) {
1010
if (key == 'userPointer') {
11-
return '*_User';
11+
return { type: 'Pointer', targetClass: '_User' };
1212
} else if (key == 'picture') {
13-
return 'file';
13+
return { type: 'File' };
1414
} else if (key == 'location') {
15-
return 'geopoint';
15+
return { type: 'GeoPoint' };
1616
}
1717
return;
1818
},

src/Adapters/Storage/Mongo/MongoSchemaCollection.js

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,33 @@ function parseFieldTypeToMongoFieldType({ type, targetClass }) {
9393
}
9494
}
9595

96+
// Returns { code, error } if invalid, or { result }, an object
97+
// suitable for inserting into _SCHEMA collection, otherwise.
98+
function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPermissions) {
99+
100+
let mongoObject = {
101+
_id: className,
102+
objectId: 'string',
103+
updatedAt: 'string',
104+
createdAt: 'string'
105+
};
106+
107+
for (let fieldName in fields) {
108+
mongoObject[fieldName] = parseFieldTypeToMongoFieldType(fields[fieldName]);
109+
}
110+
111+
if (typeof classLevelPermissions !== 'undefined') {
112+
mongoObject._metadata = mongoObject._metadata || {};
113+
if (!classLevelPermissions) {
114+
delete mongoObject._metadata.class_permissions;
115+
} else {
116+
mongoObject._metadata.class_permissions = classLevelPermissions;
117+
}
118+
}
119+
120+
return mongoObject;
121+
}
122+
96123
class MongoSchemaCollection {
97124
_collection: MongoCollection;
98125

@@ -136,8 +163,9 @@ class MongoSchemaCollection {
136163
// later PR. Returns a promise that is expected to resolve with the newly created schema, in Parse format.
137164
// If the class already exists, returns a promise that rejects with undefined as the reason. If the collection
138165
// can't be added for a reason other than it already existing, requirements for rejection reason are TBD.
139-
addSchema(name: string, fields) {
140-
let mongoObject = _mongoSchemaObjectFromNameFields(name, fields);
166+
addSchema(name: string, fields, classLevelPermissions) {
167+
let mongoSchema = mongoSchemaFromFieldsAndClassNameAndCLP(fields, name, classLevelPermissions);
168+
let mongoObject = _mongoSchemaObjectFromNameFields(name, mongoSchema);
141169
return this._collection.insertOne(mongoObject)
142170
.then(result => mongoSchemaToParseSchema(result.ops[0]))
143171
.catch(error => {
@@ -155,18 +183,27 @@ class MongoSchemaCollection {
155183
upsertSchema(name: string, query: string, update) {
156184
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
157185
}
186+
187+
updateField(className: string, fieldName: string, type: string) {
188+
// We don't have this field. Update the schema.
189+
// Note that we use the $exists guard and $set to avoid race
190+
// conditions in the database. This is important!
191+
let query = {};
192+
query[fieldName] = { '$exists': false };
193+
let update = {};
194+
if (typeof type === 'string') {
195+
type = {
196+
type: type
197+
}
198+
}
199+
update[fieldName] = parseFieldTypeToMongoFieldType(type);
200+
update = {'$set': update};
201+
return this.upsertSchema(className, query, update);
202+
}
158203
}
159204

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

164-
// Exported because we haven't moved all mongo schema format related logic
165-
// into the database adapter yet. We will remove this before too long.
166-
MongoSchemaCollection._DONOTUSEmongoFieldToParseSchemaField = mongoFieldToParseSchemaField
167-
168-
// Exported because we haven't moved all mongo schema format related logic
169-
// into the database adapter yet. We will remove this before too long.
170-
MongoSchemaCollection._DONOTUSEparseFieldTypeToMongoFieldType = parseFieldTypeToMongoFieldType;
171-
172209
export default MongoSchemaCollection

src/Controllers/DatabaseController.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,8 @@ DatabaseController.prototype.loadSchema = function(acceptor = returnsTrue) {
9090
DatabaseController.prototype.redirectClassNameForKey = function(className, key) {
9191
return this.loadSchema().then((schema) => {
9292
var t = schema.getExpectedType(className, key);
93-
var match = t ? t.match(/^relation<(.*)>$/) : false;
94-
if (match) {
95-
return match[1];
93+
if (t.type == 'Relation') {
94+
return t.targetClass;
9695
} else {
9796
return className;
9897
}
@@ -446,11 +445,10 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem
446445
let promises = Object.keys(query).map((key) => {
447446
if (query[key] && (query[key]['$in'] || query[key]['$ne'] || query[key]['$nin'] || query[key].__type == 'Pointer')) {
448447
let t = schema.getExpectedType(className, key);
449-
let match = t ? t.match(/^relation<(.*)>$/) : false;
450-
if (!match) {
448+
if (!t || t.type !== 'Relation') {
451449
return Promise.resolve(query);
452450
}
453-
let relatedClassName = match[1];
451+
let relatedClassName = t.targetClass;
454452
// Build the list of queries
455453
let queries = Object.keys(query[key]).map((constraintKey) => {
456454
let relatedIds;
@@ -599,7 +597,6 @@ DatabaseController.prototype.find = function(className, query, options = {}) {
599597
if (options.limit) {
600598
mongoOptions.limit = options.limit;
601599
}
602-
603600
let isMaster = !('acl' in options);
604601
let aclGroup = options.acl || [];
605602
let acceptor = schema => schema.hasKeys(className, keysForQuery(query))

0 commit comments

Comments
 (0)