@@ -169,47 +169,48 @@ function invalidClassNameMessage(className) {
169
169
return 'Invalid classname: ' + className + ', classnames can only have alphanumeric characters and _, and must start with an alpha character ' ;
170
170
}
171
171
172
- // Returns { error: "message", code: ### } if the type could not be
173
- // converted, otherwise returns a returns { result: "mongotype" }
174
- // where mongotype is suitable for inserting into mongo _SCHEMA collection
175
- function schemaAPITypeToMongoFieldType ( type ) {
176
- var invalidJsonError = { error : "invalid JSON" , code : Parse . Error . INVALID_JSON } ;
177
- if ( type . type == 'Pointer' ) {
178
- if ( ! type . targetClass ) {
179
- return { error : 'type Pointer needs a class name' , code : 135 } ;
180
- } else if ( typeof type . targetClass !== 'string' ) {
181
- return invalidJsonError ;
182
- } else if ( ! classNameIsValid ( type . targetClass ) ) {
183
- return { error : invalidClassNameMessage ( type . targetClass ) , code : Parse . Error . INVALID_CLASS_NAME } ;
184
- } else {
185
- return { result : '*' + type . targetClass } ;
186
- }
187
- }
188
- if ( type . type == 'Relation' ) {
189
- if ( ! type . targetClass ) {
190
- return { error : 'type Relation needs a class name' , code : 135 } ;
191
- } else if ( typeof type . targetClass !== 'string' ) {
192
- return invalidJsonError ;
193
- } else if ( ! classNameIsValid ( type . targetClass ) ) {
194
- return { error : invalidClassNameMessage ( type . targetClass ) , code : Parse . Error . INVALID_CLASS_NAME } ;
195
- } else {
196
- return { result : 'relation<' + type . targetClass + '>' } ;
197
- }
198
- }
199
- if ( typeof type . type !== 'string' ) {
200
- return { error : "invalid JSON" , code : Parse . Error . INVALID_JSON } ;
201
- }
202
- switch ( type . type ) {
203
- default : return { error : 'invalid field type: ' + type . type , code : Parse . Error . INCORRECT_TYPE } ;
204
- case 'Number' : return { result : 'number' } ;
205
- case 'String' : return { result : 'string' } ;
206
- case 'Boolean' : return { result : 'boolean' } ;
207
- case 'Date' : return { result : 'date' } ;
208
- case 'Object' : return { result : 'object' } ;
209
- case 'Array' : return { result : 'array' } ;
210
- case 'GeoPoint' : return { result : 'geopoint' } ;
211
- case 'File' : return { result : 'file' } ;
212
- }
172
+ const invalidJsonError = new Parse . Error ( Parse . Error . INVALID_JSON , "invalid JSON" ) ;
173
+ const validNonRelationOrPointerTypes = [
174
+ 'Number' ,
175
+ 'String' ,
176
+ 'Boolean' ,
177
+ 'Date' ,
178
+ 'Object' ,
179
+ 'Array' ,
180
+ 'GeoPoint' ,
181
+ 'File' ,
182
+ ] ;
183
+ // Returns an error suitable for throwing if the type is invalid
184
+ const fieldTypeIsInvalid = ( { type, targetClass } ) => {
185
+ if ( type == 'Pointer' ) {
186
+ if ( ! targetClass ) {
187
+ return new Parse . Error ( 135 , 'type Pointer needs a class name' ) ;
188
+ } else if ( typeof targetClass !== 'string' ) {
189
+ return invalidJsonError ;
190
+ } else if ( ! classNameIsValid ( targetClass ) ) {
191
+ return new Parse . Error ( Parse . Error . INVALID_CLASS_NAME , invalidClassNameMessage ( targetClass ) ) ;
192
+ } else {
193
+ return undefined ;
194
+ }
195
+ }
196
+ if ( type == 'Relation' ) {
197
+ if ( ! targetClass ) {
198
+ return new Parse . Error ( 135 , 'type Relation needs a class name' ) ;
199
+ } else if ( typeof targetClass !== 'string' ) {
200
+ return invalidJsonError ;
201
+ } else if ( ! classNameIsValid ( targetClass ) ) {
202
+ return new Parse . Error ( Parse . Error . INVALID_CLASS_NAME , invalidClassNameMessage ( targetClass ) ) ;
203
+ } else {
204
+ return undefined ;
205
+ }
206
+ }
207
+ if ( typeof type !== 'string' ) {
208
+ return invalidJsonError ;
209
+ }
210
+ if ( ! _ . includes ( validNonRelationOrPointerTypes , type ) ) {
211
+ return new Parse . Error ( Parse . Error . INCORRECT_TYPE , `invalid field type: ${ type } ` ) ;
212
+ }
213
+ return undefined ;
213
214
}
214
215
215
216
// Stores the entire schema of the app in a weird hybrid format somewhere between
@@ -244,9 +245,8 @@ class Schema {
244
245
// createdAt and updatedAt are wacky and have legacy baggage
245
246
parseFormatSchema . createdAt = { type : 'String' } ;
246
247
parseFormatSchema . updatedAt = { type : 'String' } ;
247
- this . data [ schema . className ] = _ . mapValues ( parseFormatSchema , parseField =>
248
- schemaAPITypeToMongoFieldType ( parseField ) . result
249
- ) ;
248
+ //Necessary because we still use the mongo type internally here :(
249
+ this . data [ schema . className ] = _ . mapValues ( parseFormatSchema , MongoSchemaCollection . _DONOTUSEparseFieldTypeToMongoFieldType ) ;
250
250
251
251
this . perms [ schema . className ] = schema . classLevelPermissions ;
252
252
} ) ;
@@ -397,72 +397,75 @@ class Schema {
397
397
// The className must already be validated.
398
398
// If 'freeze' is true, refuse to update the schema for this field.
399
399
validateField ( className , fieldName , type , freeze ) {
400
- // Just to check that the fieldName is valid
401
- transform . transformKey ( this , className , fieldName ) ;
402
-
403
- if ( fieldName . indexOf ( "." ) > 0 ) {
404
- // subdocument key (x.y) => ok if x is of type 'object'
405
- fieldName = fieldName . split ( "." ) [ 0 ] ;
406
- type = 'object' ;
407
- }
400
+ return this . reloadData ( ) . then ( ( ) => {
401
+ // Just to check that the fieldName is valid
402
+ transform . transformKey ( this , className , fieldName ) ;
403
+
404
+ if ( fieldName . indexOf ( "." ) > 0 ) {
405
+ // subdocument key (x.y) => ok if x is of type 'object'
406
+ fieldName = fieldName . split ( "." ) [ 0 ] ;
407
+ type = 'object' ;
408
+ }
408
409
409
- let expected = this . data [ className ] [ fieldName ] ;
410
- if ( expected ) {
411
- expected = ( expected === 'map' ? 'object' : expected ) ;
412
- if ( expected === type ) {
413
- return Promise . resolve ( this ) ;
414
- } else {
415
- throw new Parse . Error (
416
- Parse . Error . INCORRECT_TYPE ,
417
- `schema mismatch for ${ className } .${ fieldName } ; expected ${ expected } but got ${ type } `
418
- ) ;
410
+ let expected = this . data [ className ] [ fieldName ] ;
411
+ if ( expected ) {
412
+ expected = ( expected === 'map' ? 'object' : expected ) ;
413
+ if ( expected === type ) {
414
+ return Promise . resolve ( this ) ;
415
+ } else {
416
+ throw new Parse . Error (
417
+ Parse . Error . INCORRECT_TYPE ,
418
+ `schema mismatch for ${ className } .${ fieldName } ; expected ${ expected } but got ${ type } `
419
+ ) ;
420
+ }
419
421
}
420
- }
421
422
422
- if ( freeze ) {
423
- throw new Parse . Error ( Parse . Error . INVALID_JSON , `schema is frozen, cannot add ${ fieldName } field` ) ;
424
- }
423
+ if ( freeze ) {
424
+ throw new Parse . Error ( Parse . Error . INVALID_JSON , `schema is frozen, cannot add ${ fieldName } field` ) ;
425
+ }
425
426
426
- // We don't have this field, but if the value is null or undefined,
427
- // we won't update the schema until we get a value with a type.
428
- if ( ! type ) {
429
- return Promise . resolve ( this ) ;
430
- }
427
+ // We don't have this field, but if the value is null or undefined,
428
+ // we won't update the schema until we get a value with a type.
429
+ if ( ! type ) {
430
+ return Promise . resolve ( this ) ;
431
+ }
431
432
432
- if ( type === 'geopoint' ) {
433
- // Make sure there are not other geopoint fields
434
- for ( var otherKey in this . data [ className ] ) {
435
- if ( this . data [ className ] [ otherKey ] === 'geopoint' ) {
436
- throw new Parse . Error (
437
- Parse . Error . INCORRECT_TYPE ,
438
- 'there can only be one geopoint field in a class' ) ;
433
+ if ( type === 'geopoint' ) {
434
+ // Make sure there are not other geopoint fields
435
+ for ( var otherKey in this . data [ className ] ) {
436
+ if ( this . data [ className ] [ otherKey ] === 'geopoint' ) {
437
+ throw new Parse . Error (
438
+ Parse . Error . INCORRECT_TYPE ,
439
+ 'there can only be one geopoint field in a class' ) ;
440
+ }
439
441
}
440
442
}
441
- }
442
443
443
- // We don't have this field. Update the schema.
444
- // Note that we use the $exists guard and $set to avoid race
445
- // conditions in the database. This is important!
446
- let query = { } ;
447
- query [ fieldName ] = { '$exists' : false } ;
448
- var update = { } ;
449
- update [ fieldName ] = type ;
450
- update = { '$set' : update } ;
451
- return this . _collection . upsertSchema ( className , query , update ) . then ( ( ) => {
452
- // The update succeeded. Reload the schema
453
- return this . reloadData ( ) ;
454
- } , ( ) => {
455
- // The update failed. This can be okay - it might have been a race
456
- // condition where another client updated the schema in the same
457
- // way that we wanted to. So, just reload the schema
458
- return this . reloadData ( ) ;
459
- } ) . then ( ( ) => {
460
- // Ensure that the schema now validates
461
- return this . validateField ( className , fieldName , type , true ) ;
462
- } , ( error ) => {
463
- // The schema still doesn't validate. Give up
464
- throw new Parse . Error ( Parse . Error . INVALID_JSON ,
465
- 'schema key will not revalidate' ) ;
444
+ // We don't have this field. Update the schema.
445
+ // Note that we use the $exists guard and $set to avoid race
446
+ // conditions in the database. This is important!
447
+ let query = { } ;
448
+ query [ fieldName ] = { '$exists' : false } ;
449
+ var update = { } ;
450
+ update [ fieldName ] = type ;
451
+ update = { '$set' : update } ;
452
+ return this . _collection . upsertSchema ( className , query , update ) . then ( ( ) => {
453
+ // The update succeeded. Reload the schema
454
+ return this . reloadData ( ) ;
455
+ } , ( ) => {
456
+ // The update failed. This can be okay - it might have been a race
457
+ // condition where another client updated the schema in the same
458
+ // way that we wanted to. So, just reload the schema
459
+ return this . reloadData ( ) ;
460
+ } ) . then ( ( ) => {
461
+ // Ensure that the schema now validates
462
+ return this . validateField ( className , fieldName , type , true ) ;
463
+ } , ( error ) => {
464
+ // The schema still doesn't validate. Give up
465
+ console . log ( error )
466
+ throw new Parse . Error ( Parse . Error . INVALID_JSON ,
467
+ 'schema key will not revalidate' ) ;
468
+ } ) ;
466
469
} ) ;
467
470
}
468
471
@@ -551,7 +554,6 @@ class Schema {
551
554
// Every object has ACL implicitly.
552
555
continue ;
553
556
}
554
-
555
557
promise = thenValidateField ( promise , className , fieldName , expected ) ;
556
558
}
557
559
promise = thenValidateRequiredColumns ( promise , className , object , query ) ;
@@ -643,7 +645,7 @@ function load(collection) {
643
645
}
644
646
645
647
// Returns { code, error } if invalid, or { result }, an object
646
- // suitable for inserting into _SCHEMA collection, otherwise
648
+ // suitable for inserting into _SCHEMA collection, otherwise.
647
649
function mongoSchemaFromFieldsAndClassNameAndCLP ( fields , className , classLevelPermissions ) {
648
650
if ( ! classNameIsValid ( className ) ) {
649
651
return {
@@ -652,7 +654,7 @@ function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPe
652
654
} ;
653
655
}
654
656
655
- for ( var fieldName in fields ) {
657
+ for ( let fieldName in fields ) {
656
658
if ( ! fieldNameIsValid ( fieldName ) ) {
657
659
return {
658
660
code : Parse . Error . INVALID_KEY_NAME ,
@@ -665,6 +667,8 @@ function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPe
665
667
error : 'field ' + fieldName + ' cannot be added' ,
666
668
} ;
667
669
}
670
+ const error = fieldTypeIsInvalid ( fields [ fieldName ] ) ;
671
+ if ( error ) return { code : error . code , error : error . message } ;
668
672
}
669
673
670
674
var mongoObject = {
@@ -675,19 +679,11 @@ function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPe
675
679
} ;
676
680
677
681
for ( var fieldName in defaultColumns [ className ] ) {
678
- var validatedField = schemaAPITypeToMongoFieldType ( defaultColumns [ className ] [ fieldName ] ) ;
679
- if ( ! validatedField . result ) {
680
- return validatedField ;
681
- }
682
- mongoObject [ fieldName ] = validatedField . result ;
682
+ mongoObject [ fieldName ] = MongoSchemaCollection . _DONOTUSEparseFieldTypeToMongoFieldType ( defaultColumns [ className ] [ fieldName ] ) ;
683
683
}
684
684
685
685
for ( var fieldName in fields ) {
686
- var validatedField = schemaAPITypeToMongoFieldType ( fields [ fieldName ] ) ;
687
- if ( ! validatedField . result ) {
688
- return validatedField ;
689
- }
690
- mongoObject [ fieldName ] = validatedField . result ;
686
+ mongoObject [ fieldName ] = MongoSchemaCollection . _DONOTUSEparseFieldTypeToMongoFieldType ( fields [ fieldName ] ) ;
691
687
}
692
688
693
689
var geoPoints = Object . keys ( mongoObject ) . filter ( key => mongoObject [ key ] === 'geopoint' ) ;
@@ -845,7 +841,6 @@ export {
845
841
load ,
846
842
classNameIsValid ,
847
843
invalidClassNameMessage ,
848
- schemaAPITypeToMongoFieldType ,
849
844
buildMergedSchemaObject ,
850
845
systemClasses ,
851
846
defaultColumns ,
0 commit comments