diff --git a/spec/GridFSBucketStorageAdapter.spec.js b/spec/GridFSBucketStorageAdapter.spec.js index 7e9c84a59e..d30415edf3 100644 --- a/spec/GridFSBucketStorageAdapter.spec.js +++ b/spec/GridFSBucketStorageAdapter.spec.js @@ -421,6 +421,14 @@ describe_only_db('mongo')('GridFSBucket', () => { expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile); }); + it('properly upload a file when disableIndexFieldValidation exist in databaseOptions', async () => { + const gfsAdapter = new GridFSBucketAdapter(databaseURI, { disableIndexFieldValidation: true }); + const twoMegabytesFile = randomString(2048 * 1024); + await gfsAdapter.createFile('myFileName', twoMegabytesFile); + const gfsResult = await gfsAdapter.getFileData('myFileName'); + expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile); + }); + it('properly deletes a file from GridFS', async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); await gfsAdapter.createFile('myFileName', 'a simple file'); diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 167f3ff19a..7891fa847e 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -3008,6 +3008,7 @@ describe('schemas', () => { beforeEach(async () => { await TestUtils.destroyAllDataPermanently(false); await config.database.adapter.performInitialization({ VolatileClassesSchemas: [] }); + databaseAdapter.disableIndexFieldValidation = false; }); it('cannot create index if field does not exist', done => { @@ -3036,6 +3037,29 @@ describe('schemas', () => { }); }); + it('can create index if field does not exist with disableIndexFieldValidation true ', async () => { + databaseAdapter.disableIndexFieldValidation = true; + await request({ + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', + headers: masterKeyHeaders, + json: true, + body: {}, + }); + const response = await request({ + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', + headers: masterKeyHeaders, + json: true, + body: { + indexes: { + name1: { aString: 1 }, + }, + }, + }); + expect(response.data.indexes.name1).toEqual({ aString: 1 }); + }); + it('can create index on default field', done => { request({ url: 'http://localhost:8378/1/schemas/NewClass', diff --git a/src/Adapters/Files/GridFSBucketAdapter.js b/src/Adapters/Files/GridFSBucketAdapter.js index 242fc08a0d..45a585ecc2 100644 --- a/src/Adapters/Files/GridFSBucketAdapter.js +++ b/src/Adapters/Files/GridFSBucketAdapter.js @@ -37,7 +37,7 @@ export class GridFSBucketAdapter extends FilesAdapter { const defaultMongoOptions = { }; const _mongoOptions = Object.assign(defaultMongoOptions, mongoOptions); - for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS']) { + for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS', 'disableIndexFieldValidation']) { delete _mongoOptions[key]; } this._mongoOptions = _mongoOptions; diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 2c82a2019c..481d5257d9 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -140,6 +140,7 @@ export class MongoStorageAdapter implements StorageAdapter { canSortOnJoinTables: boolean; enableSchemaHooks: boolean; schemaCacheTtl: ?number; + disableIndexFieldValidation: boolean; constructor({ uri = defaults.DefaultMongoURI, collectionPrefix = '', mongoOptions = {} }: any) { this._uri = uri; @@ -152,7 +153,8 @@ export class MongoStorageAdapter implements StorageAdapter { this.canSortOnJoinTables = true; this.enableSchemaHooks = !!mongoOptions.enableSchemaHooks; this.schemaCacheTtl = mongoOptions.schemaCacheTtl; - for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS']) { + this.disableIndexFieldValidation = !!mongoOptions.disableIndexFieldValidation; + for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS', 'disableIndexFieldValidation']) { delete mongoOptions[key]; delete this._mongoOptions[key]; } @@ -289,6 +291,7 @@ export class MongoStorageAdapter implements StorageAdapter { } else { Object.keys(field).forEach(key => { if ( + !this.disableIndexFieldValidation && !Object.prototype.hasOwnProperty.call( fields, key.indexOf('_p_') === 0 ? key.replace('_p_', '') : key diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index b13179553f..c7ca77b639 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -862,13 +862,16 @@ export class PostgresStorageAdapter implements StorageAdapter { _stream: any; _uuid: any; schemaCacheTtl: ?number; + disableIndexFieldValidation: boolean; constructor({ uri, collectionPrefix = '', databaseOptions = {} }: any) { const options = { ...databaseOptions }; this._collectionPrefix = collectionPrefix; this.enableSchemaHooks = !!databaseOptions.enableSchemaHooks; + this.disableIndexFieldValidation = !!databaseOptions.disableIndexFieldValidation; + this.schemaCacheTtl = databaseOptions.schemaCacheTtl; - for (const key of ['enableSchemaHooks', 'schemaCacheTtl']) { + for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'disableIndexFieldValidation']) { delete options[key]; } @@ -991,7 +994,10 @@ export class PostgresStorageAdapter implements StorageAdapter { delete existingIndexes[name]; } else { Object.keys(field).forEach(key => { - if (!Object.prototype.hasOwnProperty.call(fields, key)) { + if ( + !this.disableIndexFieldValidation && + !Object.prototype.hasOwnProperty.call(fields, key) + ) { throw new Parse.Error( Parse.Error.INVALID_QUERY, `Field ${key} does not exist, cannot add index.` @@ -1006,8 +1012,15 @@ export class PostgresStorageAdapter implements StorageAdapter { } }); await conn.tx('set-indexes-with-schema-format', async t => { - if (insertedIndexes.length > 0) { - await self.createIndexes(className, insertedIndexes, t); + try { + if (insertedIndexes.length > 0) { + await self.createIndexes(className, insertedIndexes, t); + } + } catch (e) { + const columnDoesNotExistError = e.errors && e.errors[0] && e.errors[0].code === '42703'; + if (columnDoesNotExistError && !this.disableIndexFieldValidation) { + throw e; + } } if (deletedIndexes.length > 0) { await self.dropIndexes(className, deletedIndexes, t);