Skip to content

Commit 7376c5a

Browse files
committed
Merge pull request parse-community#452 from drew-gross/schemas-put
Implement Schemas PUT
2 parents ccc1d02 + a455e1b commit 7376c5a

File tree

4 files changed

+547
-73
lines changed

4 files changed

+547
-73
lines changed

spec/Schema.spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ describe('Schema', () => {
162162
foo: 'string',
163163
})
164164
done();
165+
})
166+
.catch(error => {
167+
fail('Error creating class: ' + JSON.stringify(error));
165168
});
166169
});
167170

@@ -570,4 +573,32 @@ describe('Schema', () => {
570573
Parse.Object.enableSingleInstance();
571574
});
572575
});
576+
577+
it('can merge schemas', done => {
578+
expect(Schema.buildMergedSchemaObject({
579+
_id: 'SomeClass',
580+
someType: 'number'
581+
}, {
582+
newType: {type: 'Number'}
583+
})).toEqual({
584+
someType: {type: 'Number'},
585+
newType: {type: 'Number'},
586+
});
587+
done();
588+
});
589+
590+
it('can merge deletions', done => {
591+
expect(Schema.buildMergedSchemaObject({
592+
_id: 'SomeClass',
593+
someType: 'number',
594+
outDatedType: 'string',
595+
},{
596+
newType: {type: 'GeoPoint'},
597+
outDatedType: {__op: 'Delete'},
598+
})).toEqual({
599+
someType: {type: 'Number'},
600+
newType: {type: 'GeoPoint'},
601+
});
602+
done();
603+
});
573604
});

spec/schemas.spec.js

Lines changed: 316 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ describe('schemas', () => {
9494
headers: restKeyHeaders,
9595
}, (error, response, body) => {
9696
expect(response.statusCode).toEqual(401);
97-
expect(body.error).toEqual('unauthorized');
97+
expect(body.error).toEqual('master key not specified');
9898
done();
9999
});
100100
});
@@ -318,4 +318,319 @@ describe('schemas', () => {
318318
done();
319319
});
320320
});
321+
322+
it('requires the master key to modify schemas', done => {
323+
request.post({
324+
url: 'http://localhost:8378/1/schemas/NewClass',
325+
headers: masterKeyHeaders,
326+
json: true,
327+
body: {},
328+
}, (error, response, body) => {
329+
request.put({
330+
url: 'http://localhost:8378/1/schemas/NewClass',
331+
headers: noAuthHeaders,
332+
json: true,
333+
body: {},
334+
}, (error, response, body) => {
335+
expect(response.statusCode).toEqual(403);
336+
expect(body.error).toEqual('unauthorized');
337+
done();
338+
});
339+
});
340+
});
341+
342+
it('rejects class name mis-matches in put', done => {
343+
request.put({
344+
url: 'http://localhost:8378/1/schemas/NewClass',
345+
headers: masterKeyHeaders,
346+
json: true,
347+
body: {className: 'WrongClassName'}
348+
}, (error, response, body) => {
349+
expect(response.statusCode).toEqual(400);
350+
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
351+
expect(body.error).toEqual('class name mismatch between WrongClassName and NewClass');
352+
done();
353+
});
354+
});
355+
356+
it('refuses to add fields to non-existent classes', done => {
357+
request.put({
358+
url: 'http://localhost:8378/1/schemas/NoClass',
359+
headers: masterKeyHeaders,
360+
json: true,
361+
body: {
362+
fields: {
363+
newField: {type: 'String'}
364+
}
365+
}
366+
}, (error, response, body) => {
367+
expect(response.statusCode).toEqual(400);
368+
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
369+
expect(body.error).toEqual('class NoClass does not exist');
370+
done();
371+
});
372+
});
373+
374+
it('refuses to put to existing fields, even if it would not be a change', done => {
375+
var obj = hasAllPODobject();
376+
obj.save()
377+
.then(() => {
378+
request.put({
379+
url: 'http://localhost:8378/1/schemas/HasAllPOD',
380+
headers: masterKeyHeaders,
381+
json: true,
382+
body: {
383+
fields: {
384+
aString: {type: 'String'}
385+
}
386+
}
387+
}, (error, response, body) => {
388+
expect(response.statusCode).toEqual(400);
389+
expect(body.code).toEqual(255);
390+
expect(body.error).toEqual('field aString exists, cannot update');
391+
done();
392+
});
393+
})
394+
});
395+
396+
it('refuses to delete non-existant fields', done => {
397+
var obj = hasAllPODobject();
398+
obj.save()
399+
.then(() => {
400+
request.put({
401+
url: 'http://localhost:8378/1/schemas/HasAllPOD',
402+
headers: masterKeyHeaders,
403+
json: true,
404+
body: {
405+
fields: {
406+
nonExistantKey: {__op: "Delete"},
407+
}
408+
}
409+
}, (error, response, body) => {
410+
expect(response.statusCode).toEqual(400);
411+
expect(body.code).toEqual(255);
412+
expect(body.error).toEqual('field nonExistantKey does not exist, cannot delete');
413+
done();
414+
});
415+
});
416+
});
417+
418+
it('refuses to add a geopoint to a class that already has one', done => {
419+
var obj = hasAllPODobject();
420+
obj.save()
421+
.then(() => {
422+
request.put({
423+
url: 'http://localhost:8378/1/schemas/HasAllPOD',
424+
headers: masterKeyHeaders,
425+
json: true,
426+
body: {
427+
fields: {
428+
newGeo: {type: 'GeoPoint'}
429+
}
430+
}
431+
}, (error, response, body) => {
432+
expect(response.statusCode).toEqual(400);
433+
expect(body.code).toEqual(Parse.Error.INCORRECT_TYPE);
434+
expect(body.error).toEqual('currently, only one GeoPoint field may exist in an object. Adding newGeo when aGeoPoint already exists.');
435+
done();
436+
});
437+
});
438+
});
439+
440+
it('refuses to add two geopoints', done => {
441+
var obj = new Parse.Object('NewClass');
442+
obj.set('aString', 'aString');
443+
obj.save()
444+
.then(() => {
445+
request.put({
446+
url: 'http://localhost:8378/1/schemas/NewClass',
447+
headers: masterKeyHeaders,
448+
json: true,
449+
body: {
450+
fields: {
451+
newGeo1: {type: 'GeoPoint'},
452+
newGeo2: {type: 'GeoPoint'},
453+
}
454+
}
455+
}, (error, response, body) => {
456+
expect(response.statusCode).toEqual(400);
457+
expect(body.code).toEqual(Parse.Error.INCORRECT_TYPE);
458+
expect(body.error).toEqual('currently, only one GeoPoint field may exist in an object. Adding newGeo2 when newGeo1 already exists.');
459+
done();
460+
});
461+
});
462+
});
463+
464+
it('allows you to delete and add a geopoint in the same request', done => {
465+
var obj = new Parse.Object('NewClass');
466+
obj.set('geo1', new Parse.GeoPoint({latitude: 0, longitude: 0}));
467+
obj.save()
468+
.then(() => {
469+
request.put({
470+
url: 'http://localhost:8378/1/schemas/NewClass',
471+
headers: masterKeyHeaders,
472+
json: true,
473+
body: {
474+
fields: {
475+
geo2: {type: 'GeoPoint'},
476+
geo1: {__op: 'Delete'}
477+
}
478+
}
479+
}, (error, response, body) => {
480+
expect(dd(body, {
481+
"className": "NewClass",
482+
"fields": {
483+
"ACL": {"type": "ACL"},
484+
"createdAt": {"type": "Date"},
485+
"objectId": {"type": "String"},
486+
"updatedAt": {"type": "Date"},
487+
"geo2": {"type": "GeoPoint"},
488+
}
489+
})).toEqual(undefined);
490+
done();
491+
});
492+
})
493+
});
494+
495+
it('put with no modifications returns all fields', done => {
496+
var obj = hasAllPODobject();
497+
obj.save()
498+
.then(() => {
499+
request.put({
500+
url: 'http://localhost:8378/1/schemas/HasAllPOD',
501+
headers: masterKeyHeaders,
502+
json: true,
503+
body: {},
504+
}, (error, response, body) => {
505+
expect(body).toEqual(plainOldDataSchema);
506+
done();
507+
});
508+
})
509+
});
510+
511+
it('lets you add fields', done => {
512+
request.post({
513+
url: 'http://localhost:8378/1/schemas/NewClass',
514+
headers: masterKeyHeaders,
515+
json: true,
516+
body: {},
517+
}, (error, response, body) => {
518+
request.put({
519+
url: 'http://localhost:8378/1/schemas/NewClass',
520+
headers: masterKeyHeaders,
521+
json: true,
522+
body: {
523+
fields: {
524+
newField: {type: 'String'}
525+
}
526+
}
527+
}, (error, response, body) => {
528+
expect(dd(body, {
529+
className: 'NewClass',
530+
fields: {
531+
"ACL": {"type": "ACL"},
532+
"createdAt": {"type": "Date"},
533+
"objectId": {"type": "String"},
534+
"updatedAt": {"type": "Date"},
535+
"newField": {"type": "String"},
536+
},
537+
})).toEqual(undefined);
538+
request.get({
539+
url: 'http://localhost:8378/1/schemas/NewClass',
540+
headers: masterKeyHeaders,
541+
json: true,
542+
}, (error, response, body) => {
543+
expect(body).toEqual({
544+
className: 'NewClass',
545+
fields: {
546+
ACL: {type: 'ACL'},
547+
createdAt: {type: 'Date'},
548+
updatedAt: {type: 'Date'},
549+
objectId: {type: 'String'},
550+
newField: {type: 'String'},
551+
}
552+
});
553+
done();
554+
});
555+
});
556+
})
557+
});
558+
559+
it('lets you delete multiple fields and add fields', done => {
560+
var obj1 = hasAllPODobject();
561+
obj1.save()
562+
.then(() => {
563+
request.put({
564+
url: 'http://localhost:8378/1/schemas/HasAllPOD',
565+
headers: masterKeyHeaders,
566+
json: true,
567+
body: {
568+
fields: {
569+
aString: {__op: 'Delete'},
570+
aNumber: {__op: 'Delete'},
571+
aNewString: {type: 'String'},
572+
aNewNumber: {type: 'Number'},
573+
aNewRelation: {type: 'Relation', targetClass: 'HasAllPOD'},
574+
aNewPointer: {type: 'Pointer', targetClass: 'HasAllPOD'},
575+
}
576+
}
577+
}, (error, response, body) => {
578+
expect(body).toEqual({
579+
className: 'HasAllPOD',
580+
fields: {
581+
//Default fields
582+
ACL: {type: 'ACL'},
583+
createdAt: {type: 'Date'},
584+
updatedAt: {type: 'Date'},
585+
objectId: {type: 'String'},
586+
//Custom fields
587+
aBool: {type: 'Boolean'},
588+
aDate: {type: 'Date'},
589+
aObject: {type: 'Object'},
590+
aArray: {type: 'Array'},
591+
aGeoPoint: {type: 'GeoPoint'},
592+
aFile: {type: 'File'},
593+
aNewNumber: {type: 'Number'},
594+
aNewString: {type: 'String'},
595+
aNewPointer: {type: 'Pointer', targetClass: 'HasAllPOD'},
596+
aNewRelation: {type: 'Relation', targetClass: 'HasAllPOD'},
597+
}
598+
});
599+
var obj2 = new Parse.Object('HasAllPOD');
600+
obj2.set('aNewPointer', obj1);
601+
var relation = obj2.relation('aNewRelation');
602+
relation.add(obj1);
603+
obj2.save().then(done); //Just need to make sure saving works on the new object.
604+
});
605+
});
606+
});
607+
608+
it('will not delete any fields if the additions are invalid', done => {
609+
var obj = hasAllPODobject();
610+
obj.save()
611+
.then(() => {
612+
request.put({
613+
url: 'http://localhost:8378/1/schemas/HasAllPOD',
614+
headers: masterKeyHeaders,
615+
json: true,
616+
body: {
617+
fields: {
618+
fakeNewField: {type: 'fake type'},
619+
aString: {__op: 'Delete'}
620+
}
621+
}
622+
}, (error, response, body) => {
623+
expect(body.code).toEqual(Parse.Error.INCORRECT_TYPE);
624+
expect(body.error).toEqual('invalid field type: fake type');
625+
request.get({
626+
url: 'http://localhost:8378/1/schemas/HasAllPOD',
627+
headers: masterKeyHeaders,
628+
json: true,
629+
}, (error, response, body) => {
630+
expect(response.body).toEqual(plainOldDataSchema);
631+
done();
632+
});
633+
});
634+
});
635+
});
321636
});

0 commit comments

Comments
 (0)