From 4fe54dc5ce0ed1d478d895ed2db1343fdf74f7f6 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 2 May 2022 12:56:27 +1000 Subject: [PATCH 01/10] feat: add `Parse.Cloud.beforeSave(Parse.File, (req) => {})` --- spec/CloudCode.Validator.spec.js | 19 +++-- spec/CloudCode.spec.js | 95 +++++++++++++++------ src/Routers/FilesRouter.js | 13 +-- src/cloud-code/Parse.Cloud.js | 137 +++++++++---------------------- src/triggers.js | 18 +--- 5 files changed, 127 insertions(+), 155 deletions(-) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index b5d5134533..196568f763 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -737,7 +737,8 @@ describe('cloud validator', () => { }); it('basic beforeSaveFile skipWithMasterKey', async done => { - Parse.Cloud.beforeSaveFile( + Parse.Cloud.beforeSave( + Parse.File, () => { throw 'beforeSaveFile should have resolved using master key.'; }, @@ -1431,7 +1432,7 @@ describe('cloud validator', () => { }); it('validate beforeSaveFile', async done => { - Parse.Cloud.beforeSaveFile(() => {}, validatorSuccess); + Parse.Cloud.beforeSave(Parse.File, () => {}, validatorSuccess); const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); @@ -1440,7 +1441,7 @@ describe('cloud validator', () => { }); it('validate beforeSaveFile fail', async done => { - Parse.Cloud.beforeSaveFile(() => {}, validatorFail); + Parse.Cloud.beforeSave(Parse.File, () => {}, validatorFail); try { const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); @@ -1452,7 +1453,7 @@ describe('cloud validator', () => { }); it('validate afterSaveFile', async done => { - Parse.Cloud.afterSaveFile(() => {}, validatorSuccess); + Parse.Cloud.afterSave(Parse.File, () => {}, validatorSuccess); const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); @@ -1461,7 +1462,7 @@ describe('cloud validator', () => { }); it('validate afterSaveFile fail', async done => { - Parse.Cloud.beforeSaveFile(() => {}, validatorFail); + Parse.Cloud.beforeSave(Parse.File, () => {}, validatorFail); try { const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); @@ -1473,7 +1474,7 @@ describe('cloud validator', () => { }); it('validate beforeDeleteFile', async done => { - Parse.Cloud.beforeDeleteFile(() => {}, validatorSuccess); + Parse.Cloud.beforeDelete(Parse.File, () => {}, validatorSuccess); const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save(); @@ -1482,7 +1483,7 @@ describe('cloud validator', () => { }); it('validate beforeDeleteFile fail', async done => { - Parse.Cloud.beforeDeleteFile(() => {}, validatorFail); + Parse.Cloud.beforeDelete(Parse.File, () => {}, validatorFail); try { const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save(); @@ -1495,7 +1496,7 @@ describe('cloud validator', () => { }); it('validate afterDeleteFile', async done => { - Parse.Cloud.afterDeleteFile(() => {}, validatorSuccess); + Parse.Cloud.afterDelete(Parse.File, () => {}, validatorSuccess); const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save(); @@ -1504,7 +1505,7 @@ describe('cloud validator', () => { }); it('validate afterDeleteFile fail', async done => { - Parse.Cloud.afterDeleteFile(() => {}, validatorFail); + Parse.Cloud.afterDelete(Parse.File, () => {}, validatorFail); try { const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save(); diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index faaa6b826a..0aac8bcf84 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -3379,7 +3379,7 @@ describe('saveFile hooks', () => { it('beforeSaveFile should return file that is already saved and not save anything to files adapter', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); - Parse.Cloud.beforeSaveFile(() => { + Parse.Cloud.beforeSave(Parse.File, () => { const newFile = new Parse.File('some-file.txt'); newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; return newFile; @@ -3394,7 +3394,7 @@ describe('saveFile hooks', () => { it('beforeSaveFile should throw error', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - Parse.Cloud.beforeSaveFile(() => { + Parse.Cloud.beforeSave(Parse.File, () => { throw new Parse.Error(400, 'some-error-message'); }); const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); @@ -3408,8 +3408,8 @@ describe('saveFile hooks', () => { it('beforeSaveFile should change values of uploaded file by editing fileObject directly', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); - Parse.Cloud.beforeSaveFile(async req => { - expect(req.triggerName).toEqual('beforeSaveFile'); + Parse.Cloud.beforeSave(Parse.File, async req => { + expect(req.triggerName).toEqual('beforeSave'); expect(req.master).toBe(true); req.file.addMetadata('foo', 'bar'); req.file.addTag('tagA', 'some-tag'); @@ -3437,8 +3437,8 @@ describe('saveFile hooks', () => { it('beforeSaveFile should change values by returning new fileObject', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); - Parse.Cloud.beforeSaveFile(async req => { - expect(req.triggerName).toEqual('beforeSaveFile'); + Parse.Cloud.beforeSave(Parse.File, async req => { + expect(req.triggerName).toEqual('beforeSave'); expect(req.fileSize).toBe(3); const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6], 'application/pdf'); newFile.setMetadata({ foo: 'bar' }); @@ -3471,8 +3471,8 @@ describe('saveFile hooks', () => { it('beforeSaveFile should contain metadata and tags saved from client', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); - Parse.Cloud.beforeSaveFile(async req => { - expect(req.triggerName).toEqual('beforeSaveFile'); + Parse.Cloud.beforeSave(Parse.File, async req => { + expect(req.triggerName).toEqual('beforeSave'); expect(req.fileSize).toBe(3); expect(req.file).toBeInstanceOf(Parse.File); expect(req.file.name()).toBe('popeye.txt'); @@ -3500,7 +3500,7 @@ describe('saveFile hooks', () => { await reconfigureServer({ filesAdapter: mockAdapter }); const config = Config.get('test'); config.filesController.options.preserveFileName = true; - Parse.Cloud.beforeSaveFile(async ({ file }) => { + Parse.Cloud.beforeSave(Parse.File, async ({ file }) => { expect(file.name()).toBe('popeye.txt'); const fileData = await file.getData(); const newFile = new Parse.File('2020-04-01.txt', { base64: fileData }); @@ -3514,13 +3514,13 @@ describe('saveFile hooks', () => { it('afterSaveFile should set fileSize to null if beforeSave returns an already saved file', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); - Parse.Cloud.beforeSaveFile(req => { + Parse.Cloud.beforeSave(Parse.File, req => { expect(req.fileSize).toBe(3); const newFile = new Parse.File('some-file.txt'); newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; return newFile; }); - Parse.Cloud.afterSaveFile(req => { + Parse.Cloud.afterSave(Parse.File, req => { expect(req.fileSize).toBe(null); }); const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); @@ -3533,7 +3533,7 @@ describe('saveFile hooks', () => { it('afterSaveFile should throw error', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - Parse.Cloud.afterSaveFile(async () => { + Parse.Cloud.afterSave(Parse.File, async () => { throw new Parse.Error(400, 'some-error-message'); }); const filename = 'donald_duck.pdf'; @@ -3547,11 +3547,11 @@ describe('saveFile hooks', () => { it('afterSaveFile should call with fileObject', async done => { await reconfigureServer({ filesAdapter: mockAdapter }); - Parse.Cloud.beforeSaveFile(async req => { + Parse.Cloud.beforeSave(Parse.File, async req => { req.file.setTags({ tagA: 'some-tag' }); req.file.setMetadata({ foo: 'bar' }); }); - Parse.Cloud.afterSaveFile(async req => { + Parse.Cloud.afterSave(Parse.File, async req => { expect(req.master).toBe(true); expect(req.file._tags).toEqual({ tagA: 'some-tag' }); expect(req.file._metadata).toEqual({ foo: 'bar' }); @@ -3563,13 +3563,13 @@ describe('saveFile hooks', () => { it('afterSaveFile should change fileSize when file data changes', async done => { await reconfigureServer({ filesAdapter: mockAdapter }); - Parse.Cloud.beforeSaveFile(async req => { + Parse.Cloud.beforeSave(Parse.File, async req => { expect(req.fileSize).toBe(3); expect(req.master).toBe(true); const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6, 7, 8, 9], 'application/pdf'); return newFile; }); - Parse.Cloud.afterSaveFile(async req => { + Parse.Cloud.afterSave(Parse.File, async req => { expect(req.fileSize).toBe(6); expect(req.master).toBe(true); done(); @@ -3580,7 +3580,7 @@ describe('saveFile hooks', () => { it('beforeDeleteFile should call with fileObject', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - Parse.Cloud.beforeDeleteFile(req => { + Parse.Cloud.beforeDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); expect(req.file._name).toEqual('popeye.txt'); expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); @@ -3592,7 +3592,7 @@ describe('saveFile hooks', () => { it('beforeDeleteFile should throw error', async done => { await reconfigureServer({ filesAdapter: mockAdapter }); - Parse.Cloud.beforeDeleteFile(() => { + Parse.Cloud.beforeDelete(Parse.File, () => { throw new Error('some error message'); }); const file = new Parse.File('popeye.txt'); @@ -3606,12 +3606,12 @@ describe('saveFile hooks', () => { it('afterDeleteFile should call with fileObject', async done => { await reconfigureServer({ filesAdapter: mockAdapter }); - Parse.Cloud.beforeDeleteFile(req => { + Parse.Cloud.beforeDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); expect(req.file._name).toEqual('popeye.txt'); expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); }); - Parse.Cloud.afterDeleteFile(req => { + Parse.Cloud.afterDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); expect(req.file._name).toEqual('popeye.txt'); expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); @@ -3623,7 +3623,7 @@ describe('saveFile hooks', () => { it('beforeSaveFile should not change file if nothing is returned', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - Parse.Cloud.beforeSaveFile(() => { + Parse.Cloud.beforeSave(Parse.File, () => { return; }); const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); @@ -3632,7 +3632,7 @@ describe('saveFile hooks', () => { }); it('throw custom error from beforeSaveFile', async done => { - Parse.Cloud.beforeSaveFile(() => { + Parse.Cloud.beforeSave(Parse.File, () => { throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); }); try { @@ -3646,7 +3646,7 @@ describe('saveFile hooks', () => { }); it('throw empty error from beforeSaveFile', async done => { - Parse.Cloud.beforeSaveFile(() => { + Parse.Cloud.beforeSave(Parse.File, () => { throw null; }); try { @@ -3658,6 +3658,55 @@ describe('saveFile hooks', () => { done(); } }); + + it('legacy hooks', async () => { + await reconfigureServer({ filesAdapter: mockAdapter }); + const logger = require('../lib/logger').logger; + const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); + const triggers = { + beforeSaveFile(req) { + req.file.setTags({ tagA: 'some-tag' }); + req.file.setMetadata({ foo: 'bar' }); + expect(req.triggerName).toEqual('beforeSave'); + expect(req.master).toBe(true); + }, + afterSaveFile(req) { + expect(req.master).toBe(true); + expect(req.file._tags).toEqual({ tagA: 'some-tag' }); + expect(req.file._metadata).toEqual({ foo: 'bar' }); + }, + beforeDeleteFile(req) { + expect(req.file).toBeInstanceOf(Parse.File); + expect(req.file._name).toEqual('popeye.txt'); + expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); + expect(req.fileSize).toBe(null); + }, + afterDeleteFile(req) { + expect(req.file).toBeInstanceOf(Parse.File); + expect(req.file._name).toEqual('popeye.txt'); + expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); + }, + }; + + for (const key in triggers) { + spyOn(triggers, key).and.callThrough(); + Parse.Cloud[key](triggers[key]); + } + + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + await file.save({ useMasterKey: true }); + await file.destroy({ useMasterKey: true }); + await new Promise(resolve => setTimeout(resolve, 100)); + for (const key in triggers) { + expect(triggers[key]).toHaveBeenCalled(); + expect(logSpy).toHaveBeenCalledWith( + `DeprecationWarning: Parse.Cloud.${key} is deprecated and will be removed in a future version. Use Parse.Cloud.${key.replace( + 'File', + '' + )}(Parse.File, (req) => {})` + ); + } + }); }); describe('sendEmail', () => { diff --git a/src/Routers/FilesRouter.js b/src/Routers/FilesRouter.js index c0c7e00f13..cccf8e5504 100644 --- a/src/Routers/FilesRouter.js +++ b/src/Routers/FilesRouter.js @@ -141,7 +141,7 @@ export class FilesRouter { try { // run beforeSaveFile trigger const triggerResult = await triggers.maybeRunFileTrigger( - triggers.Types.beforeSaveFile, + triggers.Types.beforeSave, fileObject, config, req.auth @@ -194,12 +194,7 @@ export class FilesRouter { }; } // run afterSaveFile trigger - await triggers.maybeRunFileTrigger( - triggers.Types.afterSaveFile, - fileObject, - config, - req.auth - ); + await triggers.maybeRunFileTrigger(triggers.Types.afterSave, fileObject, config, req.auth); res.status(201); res.set('Location', saveResult.url); res.json(saveResult); @@ -222,7 +217,7 @@ export class FilesRouter { file._url = filesController.adapter.getFileLocation(req.config, filename); const fileObject = { file, fileSize: null }; await triggers.maybeRunFileTrigger( - triggers.Types.beforeDeleteFile, + triggers.Types.beforeDelete, fileObject, req.config, req.auth @@ -231,7 +226,7 @@ export class FilesRouter { await filesController.deleteFile(req.config, filename); // run afterDeleteFile trigger await triggers.maybeRunFileTrigger( - triggers.Types.afterDeleteFile, + triggers.Types.afterDelete, fileObject, req.config, req.auth diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 9fb437cada..ffd1c18e11 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -440,128 +440,65 @@ ParseCloud.afterFind = function (parseClass, handler, validationHandler) { ); }; -/** - * Registers a before save file function. - * - * **Available in Cloud Code only.** - * - * ``` - * Parse.Cloud.beforeSaveFile(async (request) => { - * // code here - * }, (request) => { - * // validation code here - * }); - * - * Parse.Cloud.beforeSaveFile(async (request) => { - * // code here - * }, { ...validationObject }); - *``` - * - * @method beforeSaveFile - * @name Parse.Cloud.beforeSaveFile - * @param {Function} func The function to run before saving a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}. - * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. - */ ParseCloud.beforeSaveFile = function (handler, validationHandler) { - validateValidator(validationHandler); - triggers.addFileTrigger( - triggers.Types.beforeSaveFile, - handler, - Parse.applicationId, - validationHandler - ); + Deprecator.logRuntimeDeprecation({ + usage: 'Parse.Cloud.beforeSaveFile', + solution: 'Use Parse.Cloud.beforeSave(Parse.File, (req) => {})', + }); + ParseCloud.beforeSave(Parse.File, handler, validationHandler); }; -/** - * Registers an after save file function. - * - * **Available in Cloud Code only.** - * - * ``` - * Parse.Cloud.afterSaveFile(async (request) => { - * // code here - * }, (request) => { - * // validation code here - * }); - * - * Parse.Cloud.afterSaveFile(async (request) => { - * // code here - * }, { ...validationObject }); - *``` - * - * @method afterSaveFile - * @name Parse.Cloud.afterSaveFile - * @param {Function} func The function to run after saving a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}. - * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. - */ ParseCloud.afterSaveFile = function (handler, validationHandler) { - validateValidator(validationHandler); - triggers.addFileTrigger( - triggers.Types.afterSaveFile, - handler, - Parse.applicationId, - validationHandler - ); + Deprecator.logRuntimeDeprecation({ + usage: 'Parse.Cloud.afterSaveFile', + solution: 'Use Parse.Cloud.afterSave(Parse.File, (req) => {})', + }); + ParseCloud.afterSave(Parse.File, handler, validationHandler); }; -/** - * Registers a before delete file function. - * - * **Available in Cloud Code only.** - * - * ``` - * Parse.Cloud.beforeDeleteFile(async (request) => { - * // code here - * }, (request) => { - * // validation code here - * }); - * - * Parse.Cloud.beforeDeleteFile(async (request) => { - * // code here - * }, { ...validationObject }); - *``` - * - * @method beforeDeleteFile - * @name Parse.Cloud.beforeDeleteFile - * @param {Function} func The function to run before deleting a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}. - * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. - */ ParseCloud.beforeDeleteFile = function (handler, validationHandler) { - validateValidator(validationHandler); - triggers.addFileTrigger( - triggers.Types.beforeDeleteFile, - handler, - Parse.applicationId, - validationHandler - ); + Deprecator.logRuntimeDeprecation({ + usage: 'Parse.Cloud.beforeDeleteFile', + solution: 'Use Parse.Cloud.beforeDelete(Parse.File, (req) => {})', + }); + ParseCloud.beforeDelete(Parse.File, handler, validationHandler); +}; + +ParseCloud.afterDeleteFile = function (handler, validationHandler) { + Deprecator.logRuntimeDeprecation({ + usage: 'Parse.Cloud.afterDeleteFile', + solution: 'Use Parse.Cloud.afterDelete(Parse.File, (req) => {})', + }); + ParseCloud.afterDelete(Parse.File, handler, validationHandler); }; /** - * Registers an after delete file function. + * + * Registers a before create function * * **Available in Cloud Code only.** * * ``` - * Parse.Cloud.afterDeleteFile(async (request) => { + * Parse.Cloud.beforeCreate(Parse.File, (request) => { * // code here * }, (request) => { * // validation code here * }); * - * Parse.Cloud.afterDeleteFile(async (request) => { - * // code here - * }, { ...validationObject }); - *``` + * ``` * - * @method afterDeleteFile - * @name Parse.Cloud.afterDeleteFile - * @param {Function} func The function to after before deleting a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}. - * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. + * @method beforeCreate + * @name Parse.Cloud.beforeCreate + * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after save function for. This can instead be a String that is the className of the subclass. + * @param {Function} func The function to run before a save. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; + * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ -ParseCloud.afterDeleteFile = function (handler, validationHandler) { +ParseCloud.beforeCreate = function (_, handler, validationHandler) { + const className = triggers.getClassName(Parse.File); validateValidator(validationHandler); - triggers.addFileTrigger( - triggers.Types.afterDeleteFile, + triggers.addTrigger( + triggers.Types.beforeCreate, + className, handler, Parse.applicationId, validationHandler diff --git a/src/triggers.js b/src/triggers.js index 360166d0aa..5da736bb40 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -12,10 +12,6 @@ export const Types = { afterDelete: 'afterDelete', beforeFind: 'beforeFind', afterFind: 'afterFind', - beforeSaveFile: 'beforeSaveFile', - afterSaveFile: 'afterSaveFile', - beforeDeleteFile: 'beforeDeleteFile', - afterDeleteFile: 'afterDeleteFile', beforeConnect: 'beforeConnect', beforeSubscribe: 'beforeSubscribe', afterEvent: 'afterEvent', @@ -50,6 +46,9 @@ export function getClassName(parseClass) { if (parseClass && parseClass.className) { return parseClass.className; } + if (parseClass && parseClass.name) { + return parseClass.name.replace('Parse', '@'); + } return parseClass; } @@ -140,11 +139,6 @@ export function addTrigger(type, className, handler, applicationId, validationHa add(Category.Validators, `${type}.${className}`, validationHandler, applicationId); } -export function addFileTrigger(type, handler, applicationId, validationHandler) { - add(Category.Triggers, `${type}.${FileClassName}`, handler, applicationId); - add(Category.Validators, `${type}.${FileClassName}`, validationHandler, applicationId); -} - export function addConnectTrigger(type, handler, applicationId, validationHandler) { add(Category.Triggers, `${type}.${ConnectClassName}`, handler, applicationId); add(Category.Validators, `${type}.${ConnectClassName}`, validationHandler, applicationId); @@ -207,10 +201,6 @@ export async function runTrigger(trigger, name, request, auth) { return await trigger(request); } -export function getFileTrigger(type, applicationId) { - return getTrigger(FileClassName, type, applicationId); -} - export function triggerExists(className: string, type: string, applicationId: string): boolean { return getTrigger(className, type, applicationId) != undefined; } @@ -961,7 +951,7 @@ export function getRequestFileObject(triggerType, auth, fileObject, config) { } export async function maybeRunFileTrigger(triggerType, fileObject, config, auth) { - const fileTrigger = getFileTrigger(triggerType, config.applicationId); + const fileTrigger = getTrigger(FileClassName, triggerType, config.applicationId); if (typeof fileTrigger === 'function') { try { const request = getRequestFileObject(triggerType, auth, fileObject, config); From 066a15292c49f94cc0f15e6ca4fa7caaf6e7de24 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 2 May 2022 13:00:26 +1000 Subject: [PATCH 02/10] Update Parse.Cloud.js --- src/cloud-code/Parse.Cloud.js | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index ffd1c18e11..581c730456 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -472,39 +472,6 @@ ParseCloud.afterDeleteFile = function (handler, validationHandler) { ParseCloud.afterDelete(Parse.File, handler, validationHandler); }; -/** - * - * Registers a before create function - * - * **Available in Cloud Code only.** - * - * ``` - * Parse.Cloud.beforeCreate(Parse.File, (request) => { - * // code here - * }, (request) => { - * // validation code here - * }); - * - * ``` - * - * @method beforeCreate - * @name Parse.Cloud.beforeCreate - * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after save function for. This can instead be a String that is the className of the subclass. - * @param {Function} func The function to run before a save. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; - * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. - */ -ParseCloud.beforeCreate = function (_, handler, validationHandler) { - const className = triggers.getClassName(Parse.File); - validateValidator(validationHandler); - triggers.addTrigger( - triggers.Types.beforeCreate, - className, - handler, - Parse.applicationId, - validationHandler - ); -}; - /** * Registers a before live query server connect function. * From 70689b854b86bd88bcb87f1470f7a0d3ed38cb15 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 2 May 2022 13:02:47 +1000 Subject: [PATCH 03/10] Update DEPRECATIONS.md --- DEPRECATIONS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DEPRECATIONS.md b/DEPRECATIONS.md index dc913dd0e7..f36bb4c451 100644 --- a/DEPRECATIONS.md +++ b/DEPRECATIONS.md @@ -8,6 +8,7 @@ The following is a list of deprecations, according to the [Deprecation Policy](h | DEPPS2 | Config option `directAccess` defaults to `true` | [#6636](https://github.com/parse-community/parse-server/pull/6636) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | | DEPPS3 | Config option `enforcePrivateUsers` defaults to `true` | [#7319](https://github.com/parse-community/parse-server/pull/7319) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | | DEPPS4 | Remove convenience method for http request `Parse.Cloud.httpRequest` | [#7589](https://github.com/parse-community/parse-server/pull/7589) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | +| DEPPS5 | Remove legacy file triggers in preference to `Parse.Cloud.beforeSave(Parse.File, (req) => {})` | [#7966](https://github.com/parse-community/parse-server/pull/7966) | 5.3.0 (2022) | 7.0.0 (2024) | deprecated | - | [i_deprecation]: ## "The version and date of the deprecation." [i_removal]: ## "The version and date of the planned removal." From 15a57bcb3963223e32cc30cb060dfc9162ccfc00 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 2 May 2022 19:44:06 +1000 Subject: [PATCH 04/10] Update CloudCode.spec.js --- spec/CloudCode.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 0aac8bcf84..c4ba35fcbf 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -3695,7 +3695,8 @@ describe('saveFile hooks', () => { const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - await file.destroy({ useMasterKey: true }); + const file2 = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + await file2.destroy({ useMasterKey: true }); await new Promise(resolve => setTimeout(resolve, 100)); for (const key in triggers) { expect(triggers[key]).toHaveBeenCalled(); From 6d834a9323b425f04cf747bc1c710672fc2c42dd Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 2 May 2022 19:44:34 +1000 Subject: [PATCH 05/10] Update CloudCode.spec.js --- spec/CloudCode.spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index c4ba35fcbf..f4d955a1bf 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -3695,8 +3695,7 @@ describe('saveFile hooks', () => { const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - const file2 = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); - await file2.destroy({ useMasterKey: true }); + await new Parse.File('popeye.txt', [1, 2, 3], 'text/plain').destroy({ useMasterKey: true }); await new Promise(resolve => setTimeout(resolve, 100)); for (const key in triggers) { expect(triggers[key]).toHaveBeenCalled(); From d6176af2aecb096f340ef59233a833bd1edaf457 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 5 May 2022 17:30:09 +1000 Subject: [PATCH 06/10] changes --- DEPRECATIONS.md | 2 +- src/cloud-code/Parse.Cloud.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DEPRECATIONS.md b/DEPRECATIONS.md index f36bb4c451..e3347fa223 100644 --- a/DEPRECATIONS.md +++ b/DEPRECATIONS.md @@ -8,7 +8,7 @@ The following is a list of deprecations, according to the [Deprecation Policy](h | DEPPS2 | Config option `directAccess` defaults to `true` | [#6636](https://github.com/parse-community/parse-server/pull/6636) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | | DEPPS3 | Config option `enforcePrivateUsers` defaults to `true` | [#7319](https://github.com/parse-community/parse-server/pull/7319) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | | DEPPS4 | Remove convenience method for http request `Parse.Cloud.httpRequest` | [#7589](https://github.com/parse-community/parse-server/pull/7589) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | -| DEPPS5 | Remove legacy file triggers in preference to `Parse.Cloud.beforeSave(Parse.File, (req) => {})` | [#7966](https://github.com/parse-community/parse-server/pull/7966) | 5.3.0 (2022) | 7.0.0 (2024) | deprecated | - | +| DEPPS5 | Remove `Parse.Cloud.beforeSaveFile((request) => {})` in preference to `Parse.Cloud.beforeSave(Parse.File, (request) => {})` | [#7966](https://github.com/parse-community/parse-server/pull/7966) | 5.3.0 (2022) | 7.0.0 (2024) | deprecated | - | [i_deprecation]: ## "The version and date of the deprecation." [i_removal]: ## "The version and date of the planned removal." diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 581c730456..cecfc42af8 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -443,7 +443,7 @@ ParseCloud.afterFind = function (parseClass, handler, validationHandler) { ParseCloud.beforeSaveFile = function (handler, validationHandler) { Deprecator.logRuntimeDeprecation({ usage: 'Parse.Cloud.beforeSaveFile', - solution: 'Use Parse.Cloud.beforeSave(Parse.File, (req) => {})', + solution: 'Use Parse.Cloud.beforeSave(Parse.File, (request) => {})', }); ParseCloud.beforeSave(Parse.File, handler, validationHandler); }; @@ -451,7 +451,7 @@ ParseCloud.beforeSaveFile = function (handler, validationHandler) { ParseCloud.afterSaveFile = function (handler, validationHandler) { Deprecator.logRuntimeDeprecation({ usage: 'Parse.Cloud.afterSaveFile', - solution: 'Use Parse.Cloud.afterSave(Parse.File, (req) => {})', + solution: 'Use Parse.Cloud.afterSave(Parse.File, (request) => {})', }); ParseCloud.afterSave(Parse.File, handler, validationHandler); }; @@ -459,7 +459,7 @@ ParseCloud.afterSaveFile = function (handler, validationHandler) { ParseCloud.beforeDeleteFile = function (handler, validationHandler) { Deprecator.logRuntimeDeprecation({ usage: 'Parse.Cloud.beforeDeleteFile', - solution: 'Use Parse.Cloud.beforeDelete(Parse.File, (req) => {})', + solution: 'Use Parse.Cloud.beforeDelete(Parse.File, (request) => {})', }); ParseCloud.beforeDelete(Parse.File, handler, validationHandler); }; @@ -467,7 +467,7 @@ ParseCloud.beforeDeleteFile = function (handler, validationHandler) { ParseCloud.afterDeleteFile = function (handler, validationHandler) { Deprecator.logRuntimeDeprecation({ usage: 'Parse.Cloud.afterDeleteFile', - solution: 'Use Parse.Cloud.afterDelete(Parse.File, (req) => {})', + solution: 'Use Parse.Cloud.afterDelete(Parse.File, (request) => {})', }); ParseCloud.afterDelete(Parse.File, handler, validationHandler); }; From 8173d80a7c1b5c5cbf34f9cfe684560786c75462 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 5 May 2022 17:32:53 +1000 Subject: [PATCH 07/10] Update triggers.js --- src/triggers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/triggers.js b/src/triggers.js index 5da736bb40..4ba21b32ea 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -17,7 +17,6 @@ export const Types = { afterEvent: 'afterEvent', }; -const FileClassName = '@File'; const ConnectClassName = '@Connect'; const baseStore = function () { @@ -951,6 +950,7 @@ export function getRequestFileObject(triggerType, auth, fileObject, config) { } export async function maybeRunFileTrigger(triggerType, fileObject, config, auth) { + const FileClassName = getClassName(Parse.File); const fileTrigger = getTrigger(FileClassName, triggerType, config.applicationId); if (typeof fileTrigger === 'function') { try { From e2f41b5078ed06ffc247701c545d0909699fb735 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 5 May 2022 18:02:12 +1000 Subject: [PATCH 08/10] Update CloudCode.spec.js --- spec/CloudCode.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index f4d955a1bf..a7b5b880f5 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -3703,7 +3703,7 @@ describe('saveFile hooks', () => { `DeprecationWarning: Parse.Cloud.${key} is deprecated and will be removed in a future version. Use Parse.Cloud.${key.replace( 'File', '' - )}(Parse.File, (req) => {})` + )}(Parse.File, (request) => {})` ); } }); From 54620da524462509ff127c12465954b3e531adb9 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sat, 28 May 2022 16:38:44 +1000 Subject: [PATCH 09/10] Update Parse.Cloud.js --- src/cloud-code/Parse.Cloud.js | 92 +++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index cecfc42af8..b2e4e6c4d2 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -440,6 +440,29 @@ ParseCloud.afterFind = function (parseClass, handler, validationHandler) { ); }; +/** + * Registers a before save file function. + * + * **Available in Cloud Code only.** + * + * ``` + * Parse.Cloud.beforeSaveFile(async (request) => { + * // code here + * }, (request) => { + * // validation code here + * }); + * + * Parse.Cloud.beforeSaveFile(async (request) => { + * // code here + * }, { ...validationObject }); + *``` + * + * @method beforeSaveFile + * @deprecated + * @name Parse.Cloud.beforeSaveFile + * @param {Function} func The function to run before saving a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}. + * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. + */ ParseCloud.beforeSaveFile = function (handler, validationHandler) { Deprecator.logRuntimeDeprecation({ usage: 'Parse.Cloud.beforeSaveFile', @@ -448,6 +471,29 @@ ParseCloud.beforeSaveFile = function (handler, validationHandler) { ParseCloud.beforeSave(Parse.File, handler, validationHandler); }; +/** + * Registers an after save file function. + * + * **Available in Cloud Code only.** + * + * ``` + * Parse.Cloud.afterSaveFile(async (request) => { + * // code here + * }, (request) => { + * // validation code here + * }); + * + * Parse.Cloud.afterSaveFile(async (request) => { + * // code here + * }, { ...validationObject }); + *``` + * + * @method afterSaveFile + * @deprecated + * @name Parse.Cloud.afterSaveFile + * @param {Function} func The function to run after saving a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}. + * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. + */ ParseCloud.afterSaveFile = function (handler, validationHandler) { Deprecator.logRuntimeDeprecation({ usage: 'Parse.Cloud.afterSaveFile', @@ -456,6 +502,29 @@ ParseCloud.afterSaveFile = function (handler, validationHandler) { ParseCloud.afterSave(Parse.File, handler, validationHandler); }; +/** + * Registers a before delete file function. + * + * **Available in Cloud Code only.** + * + * ``` + * Parse.Cloud.beforeDeleteFile(async (request) => { + * // code here + * }, (request) => { + * // validation code here + * }); + * + * Parse.Cloud.beforeDeleteFile(async (request) => { + * // code here + * }, { ...validationObject }); + *``` + * + * @method beforeDeleteFile + * @deprecated + * @name Parse.Cloud.beforeDeleteFile + * @param {Function} func The function to run before deleting a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}. + * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. + */ ParseCloud.beforeDeleteFile = function (handler, validationHandler) { Deprecator.logRuntimeDeprecation({ usage: 'Parse.Cloud.beforeDeleteFile', @@ -464,6 +533,29 @@ ParseCloud.beforeDeleteFile = function (handler, validationHandler) { ParseCloud.beforeDelete(Parse.File, handler, validationHandler); }; +/** + * Registers an after delete file function. + * + * **Available in Cloud Code only.** + * + * ``` + * Parse.Cloud.afterDeleteFile(async (request) => { + * // code here + * }, (request) => { + * // validation code here + * }); + * + * Parse.Cloud.afterDeleteFile(async (request) => { + * // code here + * }, { ...validationObject }); + *``` + * + * @method afterDeleteFile + * @deprecated + * @name Parse.Cloud.afterDeleteFile + * @param {Function} func The function to after before deleting a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}. + * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. + */ ParseCloud.afterDeleteFile = function (handler, validationHandler) { Deprecator.logRuntimeDeprecation({ usage: 'Parse.Cloud.afterDeleteFile', From fe2f994c60827c9e3a5408d61c7c40990a899615 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sat, 28 May 2022 16:44:59 +1000 Subject: [PATCH 10/10] Update Parse.Cloud.js --- src/cloud-code/Parse.Cloud.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index b2e4e6c4d2..1ee02fdb60 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -134,7 +134,7 @@ ParseCloud.job = function (functionName, handler) { * * **Available in Cloud Code only.** * - * If you want to use beforeSave for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * If you want to use beforeSave for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User} or {@link Parse.File}), you should pass the class itself and not the String for arg1. * * ``` * Parse.Cloud.beforeSave('MyCustomClass', (request) => { @@ -171,7 +171,7 @@ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) { * * **Available in Cloud Code only.** * - * If you want to use beforeDelete for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * If you want to use beforeDelete for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User} or {@link Parse.File}), you should pass the class itself and not the String for arg1. * ``` * Parse.Cloud.beforeDelete('MyCustomClass', (request) => { * // code here @@ -300,7 +300,7 @@ ParseCloud.afterLogout = function (handler) { * * **Available in Cloud Code only.** * - * If you want to use afterSave for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * If you want to use afterSave for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User} or {@link Parse.File}), you should pass the class itself and not the String for arg1. * * ``` * Parse.Cloud.afterSave('MyCustomClass', async function(request) { @@ -337,7 +337,7 @@ ParseCloud.afterSave = function (parseClass, handler, validationHandler) { * * **Available in Cloud Code only.** * - * If you want to use afterDelete for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * If you want to use afterDelete for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User} or {@link Parse.File}), you should pass the class itself and not the String for arg1. * ``` * Parse.Cloud.afterDelete('MyCustomClass', async (request) => { * // code here @@ -373,7 +373,7 @@ ParseCloud.afterDelete = function (parseClass, handler, validationHandler) { * * **Available in Cloud Code only.** * - * If you want to use beforeFind for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * If you want to use beforeFind for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User} or {@link Parse.File}), you should pass the class itself and not the String for arg1. * ``` * Parse.Cloud.beforeFind('MyCustomClass', async (request) => { * // code here @@ -409,7 +409,7 @@ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) { * * **Available in Cloud Code only.** * - * If you want to use afterFind for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * If you want to use afterFind for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User} or {@link Parse.File}), you should pass the class itself and not the String for arg1. * ``` * Parse.Cloud.afterFind('MyCustomClass', async (request) => { * // code here @@ -632,7 +632,7 @@ ParseCloud.sendEmail = function (data) { * * **Available in Cloud Code only.** * - * If you want to use beforeSubscribe for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * If you want to use beforeSubscribe for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User} or {@link Parse.File}), you should pass the class itself and not the String for arg1. * ``` * Parse.Cloud.beforeSubscribe('MyCustomClass', (request) => { * // code here