Skip to content

Commit 285dcf4

Browse files
authored
feat: Allow saving with custom objectId (#1309)
* feat: Allow saving with custom objectId * clean up
1 parent 90f1d72 commit 285dcf4

File tree

6 files changed

+134
-2
lines changed

6 files changed

+134
-2
lines changed

integration/test/ParseObjectTest.js

+56
Original file line numberDiff line numberDiff line change
@@ -2045,4 +2045,60 @@ describe('Parse Object', () => {
20452045
expect(obj.get('string')).toBeDefined();
20462046
expect(obj.get('string')).toBeInstanceOf(String);
20472047
});
2048+
2049+
it('allowCustomObjectId', async () => {
2050+
await reconfigureServer({ allowCustomObjectId: true });
2051+
Parse.allowCustomObjectId = true;
2052+
const customId = `${Date.now()}`;
2053+
const object = new Parse.Object('TestObject');
2054+
try {
2055+
await object.save();
2056+
fail();
2057+
} catch (error) {
2058+
expect(error.message).toBe('objectId must not be empty, null or undefined');
2059+
}
2060+
object.id = customId;
2061+
object.set('foo', 'bar');
2062+
await object.save();
2063+
expect(object.id).toBe(customId);
2064+
2065+
const query = new Parse.Query('TestObject');
2066+
const result = await query.get(customId);
2067+
expect(result.get('foo')).toBe('bar');
2068+
expect(result.id).toBe(customId);
2069+
2070+
result.set('foo', 'baz');
2071+
await result.save();
2072+
2073+
const afterSave = await query.get(customId);
2074+
expect(afterSave.get('foo')).toBe('baz');
2075+
Parse.allowCustomObjectId = false;
2076+
});
2077+
2078+
it('allowCustomObjectId saveAll', async () => {
2079+
await reconfigureServer({ allowCustomObjectId: true });
2080+
Parse.allowCustomObjectId = true;
2081+
const customId1 = `${Date.now()}`;
2082+
const customId2 = `${Date.now()}`;
2083+
const obj1 = new TestObject({ foo: 'bar' });
2084+
const obj2 = new TestObject({ foo: 'baz' });
2085+
try {
2086+
await Parse.Object.saveAll([obj1, obj2]);
2087+
fail();
2088+
} catch (error) {
2089+
expect(error.message).toBe('objectId must not be empty, null or undefined');
2090+
}
2091+
obj1.id = customId1;
2092+
obj2.id = customId2;
2093+
await Parse.Object.saveAll([obj1, obj2]);
2094+
expect(obj1.id).toBe(customId1);
2095+
expect(obj2.id).toBe(customId2);
2096+
2097+
const query = new Parse.Query(TestObject);
2098+
const results = await query.find();
2099+
results.forEach(result => {
2100+
expect([customId1, customId2].includes(result.id));
2101+
});
2102+
Parse.allowCustomObjectId = false;
2103+
});
20482104
});

src/CoreManager.js

+1
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ const config: Config & { [key: string]: mixed } = {
200200
FORCE_REVOCABLE_SESSION: false,
201201
ENCRYPTED_USER: false,
202202
IDEMPOTENCY: false,
203+
ALLOW_CUSTOM_OBJECT_ID: false,
203204
};
204205

205206
function requireMethods(name: string, methods: Array<string>, controller: any) {

src/Parse.js

+11
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,17 @@ const Parse = {
199199
get idempotency() {
200200
return CoreManager.get('IDEMPOTENCY');
201201
},
202+
203+
/**
204+
* @member {boolean} Parse.allowCustomObjectId
205+
* @static
206+
*/
207+
set allowCustomObjectId(value) {
208+
CoreManager.set('ALLOW_CUSTOM_OBJECT_ID', value);
209+
},
210+
get allowCustomObjectId() {
211+
return CoreManager.get('ALLOW_CUSTOM_OBJECT_ID');
212+
},
202213
};
203214

204215
Parse.ACL = require('./ParseACL').default;

src/ParseObject.js

+23-2
Original file line numberDiff line numberDiff line change
@@ -324,10 +324,18 @@ class ParseObject {
324324
}
325325

326326
_getSaveParams(): SaveParams {
327-
const method = this.id ? 'PUT' : 'POST';
327+
let method = this.id ? 'PUT' : 'POST';
328328
const body = this._getSaveJSON();
329329
let path = 'classes/' + this.className;
330-
if (this.id) {
330+
if (CoreManager.get('ALLOW_CUSTOM_OBJECT_ID')) {
331+
if (!this.createdAt) {
332+
method = 'POST';
333+
body.objectId = this.id;
334+
} else {
335+
method = 'PUT';
336+
path += '/' + this.id;
337+
}
338+
} else if (this.id) {
331339
path += '/' + this.id;
332340
} else if (this.className === '_User') {
333341
path = 'users';
@@ -2353,6 +2361,7 @@ const DefaultController = {
23532361

23542362
const RESTController = CoreManager.getRESTController();
23552363
const stateController = CoreManager.getObjectStateController();
2364+
const allowCustomObjectId = CoreManager.get('ALLOW_CUSTOM_OBJECT_ID');
23562365

23572366
options = options || {};
23582367
options.returnStatus = options.returnStatus || true;
@@ -2375,6 +2384,12 @@ const DefaultController = {
23752384
if (el instanceof ParseFile) {
23762385
filesSaved.push(el.save(options));
23772386
} else if (el instanceof ParseObject) {
2387+
if (allowCustomObjectId && !el.id) {
2388+
throw new ParseError(
2389+
ParseError.MISSING_OBJECT_ID,
2390+
'objectId must not be empty, null or undefined'
2391+
);
2392+
}
23782393
pending.push(el);
23792394
}
23802395
});
@@ -2468,6 +2483,12 @@ const DefaultController = {
24682483
});
24692484
});
24702485
} else if (target instanceof ParseObject) {
2486+
if (allowCustomObjectId && !target.id) {
2487+
throw new ParseError(
2488+
ParseError.MISSING_OBJECT_ID,
2489+
'objectId must not be empty, null or undefined'
2490+
);
2491+
}
24712492
// generate _localId in case if cascadeSave=false
24722493
target._getId();
24732494
const localId = target._localId;

src/__tests__/Parse-test.js

+7
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@ describe('Parse module', () => {
161161
CoreManager.set('REQUEST_BATCH_SIZE', 20);
162162
});
163163

164+
it('can set allowCustomObjectId', () => {
165+
expect(Parse.allowCustomObjectId).toBe(false);
166+
Parse.allowCustomObjectId = true;
167+
expect(CoreManager.get('ALLOW_CUSTOM_OBJECT_ID')).toBe(true);
168+
Parse.allowCustomObjectId = false;
169+
});
170+
164171
it('getServerHealth', () => {
165172
const controller = {
166173
request: jest.fn(),

src/__tests__/ParseObject-test.js

+36
Original file line numberDiff line numberDiff line change
@@ -3770,4 +3770,40 @@ describe('ParseObject pin', () => {
37703770
done();
37713771
});
37723772
});
3773+
3774+
it('can allowCustomObjectId', async done => {
3775+
CoreManager.set('ALLOW_CUSTOM_OBJECT_ID', true);
3776+
const o = new ParseObject('Person');
3777+
let params = o._getSaveParams();
3778+
expect(params).toEqual({
3779+
method: 'POST',
3780+
body: { objectId: undefined },
3781+
path: 'classes/Person',
3782+
});
3783+
try {
3784+
await o.save();
3785+
done.fail();
3786+
} catch (error) {
3787+
expect(error.message).toBe('objectId must not be empty, null or undefined');
3788+
}
3789+
try {
3790+
await ParseObject.saveAll([o]);
3791+
done.fail();
3792+
} catch (error) {
3793+
expect(error.message).toBe('objectId must not be empty, null or undefined');
3794+
}
3795+
o._finishFetch({
3796+
objectId: 'CUSTOM_ID',
3797+
createdAt: { __type: 'Date', iso: new Date().toISOString() },
3798+
updatedAt: { __type: 'Date', iso: new Date().toISOString() },
3799+
});
3800+
params = o._getSaveParams();
3801+
expect(params).toEqual({
3802+
method: 'PUT',
3803+
body: {},
3804+
path: 'classes/Person/CUSTOM_ID',
3805+
});
3806+
CoreManager.set('ALLOW_CUSTOM_OBJECT_ID', false);
3807+
done();
3808+
});
37733809
});

0 commit comments

Comments
 (0)