Skip to content

Commit e6feefd

Browse files
committed
Add and test logic for adding fields to the DB
1 parent d934f3a commit e6feefd

File tree

3 files changed

+167
-7
lines changed

3 files changed

+167
-7
lines changed

ExportAdapter.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ ExportAdapter.prototype.smartFind = function(coll, where, options) {
494494

495495
var index = {};
496496
index[key] = '2d';
497+
//TODO: condiser moving index creation logic into Schema.js
497498
return coll.createIndex(index).then(() => {
498499
// Retry, but just once.
499500
return coll.find(where, options).toArray();

Schema.js

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ function schemaAPITypeToMongoFieldType(type) {
137137
return { error: "invalid JSON", code: Parse.Error.INVALID_JSON };
138138
}
139139
switch (type.type) {
140-
default : return { error: 'invalid field type: ' + type.type };
140+
default: return { error: 'invalid field type: ' + type.type };
141141
case 'Number': return { result: 'number' };
142142
case 'String': return { result: 'string' };
143143
case 'Boolean': return { result: 'boolean' };
@@ -241,14 +241,38 @@ Schema.prototype.addClassIfNotExists = function(className, fields) {
241241
}
242242
}
243243

244-
245-
246-
return this.collection.insertOne({
244+
var mongoObject = {
247245
_id: className,
248246
objectId: 'string',
249247
updatedAt: 'string',
250248
createdAt: 'string',
251-
})
249+
};
250+
for (fieldName in defaultColumns[className]) {
251+
validatedField = schemaAPITypeToMongoFieldType(defaultColumns[className][fieldName]);
252+
if (validatedField.code) {
253+
return Promise.reject(validatedField);
254+
}
255+
mongoObject[fieldName] = validatedField.result;
256+
}
257+
258+
for (fieldName in fields) {
259+
validatedField = schemaAPITypeToMongoFieldType(fields[fieldName]);
260+
if (validatedField.code) {
261+
return Promise.reject(validatedField);
262+
}
263+
mongoObject[fieldName] = validatedField.result;
264+
}
265+
266+
var geoPoints = Object.keys(mongoObject).filter(key => mongoObject[key] === 'geopoint');
267+
268+
if (geoPoints.length > 1) {
269+
return Promise.reject({
270+
code: Parse.Error.INCORRECT_TYPE,
271+
error: 'currently, only one GeoPoint field may exist in an object. Adding ' + geoPoints[1] + ' when ' + geoPoints[0] + ' already exists.',
272+
});
273+
}
274+
275+
return this.collection.insertOne(mongoObject)
252276
.then(result => result.ops[0])
253277
.catch(error => {
254278
if (error.code === 11000) { //Mongo's duplicate key error

spec/Schema.spec.js

Lines changed: 137 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// These tests check that the Schema operates correctly.
22
var Config = require('../Config');
33
var Schema = require('../Schema');
4+
var dd = require('deep-diff');
45

56
var config = new Config('test');
67

@@ -142,7 +143,8 @@ describe('Schema', () => {
142143
_id: 'NewClass',
143144
objectId: 'string',
144145
updatedAt: 'string',
145-
createdAt: 'string'
146+
createdAt: 'string',
147+
foo: 'string',
146148
})
147149
done();
148150
});
@@ -183,7 +185,8 @@ describe('Schema', () => {
183185
_id: 'NewClass',
184186
objectId: 'string',
185187
updatedAt: 'string',
186-
createdAt: 'string'
188+
createdAt: 'string',
189+
foo: 'string',
187190
});
188191
});
189192
Promise.all([p1,p2])
@@ -229,4 +232,136 @@ describe('Schema', () => {
229232
done();
230233
});
231234
});
235+
236+
it('refuses to add fields with invalid types', done => {
237+
config.database.loadSchema()
238+
.then(schema => schema.addClassIfNotExists('NewClass', {
239+
foo: {type: 7}
240+
}))
241+
.catch(error => {
242+
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
243+
expect(error.error).toEqual('invalid JSON');
244+
done();
245+
});
246+
});
247+
248+
it('refuses to add fields with invalid pointer types', done => {
249+
config.database.loadSchema()
250+
.then(schema => schema.addClassIfNotExists('NewClass', {
251+
foo: {type: 'Pointer'},
252+
}))
253+
.catch(error => {
254+
expect(error.code).toEqual(135);
255+
expect(error.error).toEqual('type Pointer needs a class name');
256+
done();
257+
});
258+
});
259+
260+
it('refuses to add fields with invalid pointer target', done => {
261+
config.database.loadSchema()
262+
.then(schema => schema.addClassIfNotExists('NewClass', {
263+
foo: {type: 'Pointer', targetClass: 7},
264+
}))
265+
.catch(error => {
266+
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
267+
expect(error.error).toEqual('invalid JSON');
268+
done();
269+
});
270+
});
271+
272+
it('refuses to add fields with invalid Relation type', done => {
273+
config.database.loadSchema()
274+
.then(schema => schema.addClassIfNotExists('NewClass', {
275+
foo: {type: 'Relation', uselessKey: 7},
276+
}))
277+
.catch(error => {
278+
expect(error.code).toEqual(135);
279+
expect(error.error).toEqual('type Relation needs a class name');
280+
done();
281+
});
282+
});
283+
284+
it('refuses to add fields with invalid relation target', done => {
285+
config.database.loadSchema()
286+
.then(schema => schema.addClassIfNotExists('NewClass', {
287+
foo: {type: 'Relation', targetClass: 7},
288+
}))
289+
.catch(error => {
290+
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
291+
expect(error.error).toEqual('invalid JSON');
292+
done();
293+
});
294+
});
295+
296+
it('refuses to add fields with uncreatable pointer target class', done => {
297+
config.database.loadSchema()
298+
.then(schema => schema.addClassIfNotExists('NewClass', {
299+
foo: {type: 'Pointer', targetClass: 'not a valid class name'},
300+
}))
301+
.catch(error => {
302+
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
303+
expect(error.error).toEqual('Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character ');
304+
done();
305+
});
306+
});
307+
308+
it('refuses to add fields with uncreatable relation target class', done => {
309+
config.database.loadSchema()
310+
.then(schema => schema.addClassIfNotExists('NewClass', {
311+
foo: {type: 'Relation', targetClass: 'not a valid class name'},
312+
}))
313+
.catch(error => {
314+
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
315+
expect(error.error).toEqual('Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character ');
316+
done();
317+
});
318+
});
319+
320+
it('will create classes', done => {
321+
config.database.loadSchema()
322+
.then(schema => schema.addClassIfNotExists('NewClass', {
323+
aNumber: {type: 'Number'},
324+
aString: {type: 'String'},
325+
aBool: {type: 'Boolean'},
326+
aDate: {type: 'Date'},
327+
aObject: {type: 'Object'},
328+
aArray: {type: 'Array'},
329+
aGeoPoint: {type: 'GeoPoint'},
330+
aFile: {type: 'File'},
331+
aPointer: {type: 'Pointer', targetClass: 'ThisClassDoesNotExistYet'},
332+
aRelation: {type: 'Relation', targetClass: 'NewClass'},
333+
}))
334+
.then(mongoObj => {
335+
expect(mongoObj).toEqual({
336+
_id: 'NewClass',
337+
objectId: 'string',
338+
createdAt: 'string',
339+
updatedAt: 'string',
340+
aNumber: 'number',
341+
aString: 'string',
342+
aBool: 'boolean',
343+
aDate: 'date',
344+
aObject: 'object',
345+
aArray: 'array',
346+
aGeoPoint: 'geopoint',
347+
aFile: 'file',
348+
aPointer: '*ThisClassDoesNotExistYet',
349+
aRelation: 'relation<NewClass>',
350+
});
351+
done();
352+
});
353+
});
354+
355+
it('refuses to create two geopoints', done => {
356+
config.database.loadSchema()
357+
.then(schema => schema.addClassIfNotExists('NewClass', {
358+
geo1: {type: 'GeoPoint'},
359+
geo2: {type: 'GeoPoint'},
360+
}))
361+
.catch(error => {
362+
expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE);
363+
expect(error.error).toEqual('currently, only one GeoPoint field may exist in an object. Adding geo2 when geo1 already exists.');
364+
done();
365+
});
366+
});
232367
});

0 commit comments

Comments
 (0)