#6101 Let users define objectId (#6177)

* #6101 Let users define objectId

* Add `allowCustomObjectId` to PS Option

* Add checking in objectId creation

* Add test

* Update docs

* Update definition

* Change default to false

* throw on empty, null, undefined

* better tests

* unused async

* removed comment

* retain comment

* Linting fix according to contributing spec.
This commit is contained in:
Rhuan
2019-12-17 19:23:18 +01:00
committed by Diamond Lewis
parent 7944ef1435
commit 8bc201d228
5 changed files with 97 additions and 11 deletions

View File

@@ -44,6 +44,66 @@ describe('rest create', () => {
}); });
}); });
it('should use objectId from client when allowCustomObjectId true', async () => {
config.allowCustomObjectId = true;
// use time as unique custom id for test reusability
const customId = `${Date.now()}`;
const obj = {
objectId: customId,
};
const {
status,
response: { objectId },
} = await rest.create(config, auth.nobody(config), 'MyClass', obj);
expect(status).toEqual(201);
expect(objectId).toEqual(customId);
});
it('should throw on invalid objectId when allowCustomObjectId true', () => {
config.allowCustomObjectId = true;
const objIdNull = {
objectId: null,
};
const objIdUndef = {
objectId: undefined,
};
const objIdEmpty = {
objectId: '',
};
const err = 'objectId must not be empty, null or undefined';
expect(() =>
rest.create(config, auth.nobody(config), 'MyClass', objIdEmpty)
).toThrowError(err);
expect(() =>
rest.create(config, auth.nobody(config), 'MyClass', objIdNull)
).toThrowError(err);
expect(() =>
rest.create(config, auth.nobody(config), 'MyClass', objIdUndef)
).toThrowError(err);
});
it('should generate objectId when not set by client with allowCustomObjectId true', async () => {
config.allowCustomObjectId = true;
const {
status,
response: { objectId },
} = await rest.create(config, auth.nobody(config), 'MyClass', {});
expect(status).toEqual(201);
expect(objectId).toBeDefined();
});
it('is backwards compatible when _id size changes', done => { it('is backwards compatible when _id size changes', done => {
rest rest
.create(config, auth.nobody(config), 'Foo', { size: 10 }) .create(config, auth.nobody(config), 'Foo', { size: 10 })

View File

@@ -17,6 +17,12 @@ module.exports.ParseServerOptions = {
action: parsers.booleanParser, action: parsers.booleanParser,
default: true, default: true,
}, },
allowCustomObjectId: {
env: 'PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID',
help: 'Enable (or disable) custom objectId, defaults to false',
action: parsers.booleanParser,
default: false,
},
allowHeaders: { allowHeaders: {
env: 'PARSE_SERVER_ALLOW_HEADERS', env: 'PARSE_SERVER_ALLOW_HEADERS',
help: 'Add headers to Access-Control-Allow-Headers', help: 'Add headers to Access-Control-Allow-Headers',

View File

@@ -2,6 +2,7 @@
* @interface ParseServerOptions * @interface ParseServerOptions
* @property {Any} accountLockout account lockout policy for failed login attempts * @property {Any} accountLockout account lockout policy for failed login attempts
* @property {Boolean} allowClientClassCreation Enable (or disable) client class creation, defaults to true * @property {Boolean} allowClientClassCreation Enable (or disable) client class creation, defaults to true
* @property {Boolean} allowCustomObjectId Enable (or disable) custom objectId, defaults to false
* @property {String[]} allowHeaders Add headers to Access-Control-Allow-Headers * @property {String[]} allowHeaders Add headers to Access-Control-Allow-Headers
* @property {Adapter<AnalyticsAdapter>} analyticsAdapter Adapter module for the analytics * @property {Adapter<AnalyticsAdapter>} analyticsAdapter Adapter module for the analytics
* @property {String} appId Your Parse Application ID * @property {String} appId Your Parse Application ID

View File

@@ -102,6 +102,10 @@ export interface ParseServerOptions {
:ENV: PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION :ENV: PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION
:DEFAULT: true */ :DEFAULT: true */
allowClientClassCreation: ?boolean; allowClientClassCreation: ?boolean;
/* Enable (or disable) custom objectId
:ENV: PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID
:DEFAULT: false */
allowCustomObjectId: ?boolean;
/* Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication /* Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication
:ENV: PARSE_SERVER_AUTH_PROVIDERS */ :ENV: PARSE_SERVER_AUTH_PROVIDERS */
auth: ?any; auth: ?any;

View File

@@ -46,17 +46,32 @@ function RestWrite(
this.storage = {}; this.storage = {};
this.runOptions = {}; this.runOptions = {};
this.context = {}; this.context = {};
if (!query && data.objectId) {
throw new Parse.Error( if (!query) {
Parse.Error.INVALID_KEY_NAME, if (this.config.allowCustomObjectId) {
'objectId is an invalid field name.' if (
); Object.prototype.hasOwnProperty.call(data, 'objectId') &&
} !data.objectId
if (!query && data.id) { ) {
throw new Parse.Error( throw new Parse.Error(
Parse.Error.INVALID_KEY_NAME, Parse.Error.MISSING_OBJECT_ID,
'id is an invalid field name.' 'objectId must not be empty, null or undefined'
); );
}
} else {
if (data.objectId) {
throw new Parse.Error(
Parse.Error.INVALID_KEY_NAME,
'objectId is an invalid field name.'
);
}
if (data.id) {
throw new Parse.Error(
Parse.Error.INVALID_KEY_NAME,
'id is an invalid field name.'
);
}
}
} }
// When the operation is complete, this.response may have several // When the operation is complete, this.response may have several