import { Parse } from 'parse/node'; import * as triggers from '../triggers'; function isParseObjectConstructor(object) { return ( typeof object === 'function' && Object.prototype.hasOwnProperty.call(object, 'className') ); } function getClassName(parseClass) { if (parseClass && parseClass.className) { return parseClass.className; } return parseClass; } /** @namespace * @name Parse * @description The Parse SDK. * see [api docs](https://docs.parseplatform.org/js/api) and [guide](https://docs.parseplatform.org/js/guide) */ /** @namespace * @name Parse.Cloud * @memberof Parse * @description The Parse Cloud Code SDK. */ var ParseCloud = {}; /** * Defines a Cloud Function. * * **Available in Cloud Code only.** * * ``` * Parse.Cloud.define('functionName', (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.define('functionName', (request) => { * // code here * }, { ...validationObject }); * ``` * * @static * @memberof Parse.Cloud * @param {String} name The name of the Cloud Function * @param {Function} data The Cloud Function to register. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}. * @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.FunctionRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.define = function (functionName, handler, validationHandler) { triggers.addFunction( functionName, handler, validationHandler, Parse.applicationId ); }; /** * Defines a Background Job. * * **Available in Cloud Code only.** * * @method job * @name Parse.Cloud.job * @param {String} name The name of the Background Job * @param {Function} func The Background Job to register. This function can be async should take a single parameters a {@link Parse.Cloud.JobRequest} * */ ParseCloud.job = function (functionName, handler) { triggers.addJob(functionName, handler, Parse.applicationId); }; /** * * Registers a before save function. * * **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. * * ``` * Parse.Cloud.beforeSave('MyCustomClass', (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.beforeSave(Parse.User, (request) => { * // code here * }, { ...validationObject }) * ``` * * @method beforeSave * @name Parse.Cloud.beforeSave * @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.beforeSave = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); triggers.addTrigger( triggers.Types.beforeSave, className, handler, Parse.applicationId, validationHandler ); }; /** * Registers a before delete function. * * **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. * ``` * Parse.Cloud.beforeDelete('MyCustomClass', (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.beforeDelete(Parse.User, (request) => { * // code here * }, { ...validationObject }) *``` * * @method beforeDelete * @name Parse.Cloud.beforeDelete * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before delete function for. This can instead be a String that is the className of the subclass. * @param {Function} func The function to run before a delete. 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.beforeDelete = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); triggers.addTrigger( triggers.Types.beforeDelete, className, handler, Parse.applicationId, validationHandler ); }; /** * * Registers the before login function. * * **Available in Cloud Code only.** * * This function provides further control * in validating a login attempt. Specifically, * it is triggered after a user enters * correct credentials (or other valid authData), * but prior to a session being generated. * * ``` * Parse.Cloud.beforeLogin((request) => { * // code here * }) * * ``` * * @method beforeLogin * @name Parse.Cloud.beforeLogin * @param {Function} func The function to run before a login. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.beforeLogin = function (handler) { let className = '_User'; if (typeof handler === 'string' || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = getClassName(handler); handler = arguments[1]; } triggers.addTrigger( triggers.Types.beforeLogin, className, handler, Parse.applicationId ); }; /** * * Registers the after login function. * * **Available in Cloud Code only.** * * This function is triggered after a user logs in successfully, * and after a _Session object has been created. * * ``` * Parse.Cloud.afterLogin((request) => { * // code here * }); * ``` * * @method afterLogin * @name Parse.Cloud.afterLogin * @param {Function} func The function to run after a login. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.afterLogin = function (handler) { let className = '_User'; if (typeof handler === 'string' || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = getClassName(handler); handler = arguments[1]; } triggers.addTrigger( triggers.Types.afterLogin, className, handler, Parse.applicationId ); }; /** * * Registers the after logout function. * * **Available in Cloud Code only.** * * This function is triggered after a user logs out. * * ``` * Parse.Cloud.afterLogout((request) => { * // code here * }); * ``` * * @method afterLogout * @name Parse.Cloud.afterLogout * @param {Function} func The function to run after a logout. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.afterLogout = function (handler) { let className = '_Session'; if (typeof handler === 'string' || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = getClassName(handler); handler = arguments[1]; } triggers.addTrigger( triggers.Types.afterLogout, className, handler, Parse.applicationId ); }; /** * Registers an after save function. * * **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. * * ``` * Parse.Cloud.afterSave('MyCustomClass', async function(request) { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.afterSave(Parse.User, async function(request) { * // code here * }, { ...validationObject }); * ``` * * @method afterSave * @name Parse.Cloud.afterSave * @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 after a save. This function can be an async function and should take just one parameter, {@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.afterSave = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); triggers.addTrigger( triggers.Types.afterSave, className, handler, Parse.applicationId, validationHandler ); }; /** * Registers an after delete function. * * **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. * ``` * Parse.Cloud.afterDelete('MyCustomClass', async (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.afterDelete(Parse.User, async (request) => { * // code here * }, { ...validationObject }); *``` * * @method afterDelete * @name Parse.Cloud.afterDelete * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after delete function for. This can instead be a String that is the className of the subclass. * @param {Function} func The function to run after a delete. This function can be async and should take just one parameter, {@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.afterDelete = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); triggers.addTrigger( triggers.Types.afterDelete, className, handler, Parse.applicationId, validationHandler ); }; /** * Registers a before find function. * * **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. * ``` * Parse.Cloud.beforeFind('MyCustomClass', async (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.beforeFind(Parse.User, async (request) => { * // code here * }, { ...validationObject }); *``` * * @method beforeFind * @name Parse.Cloud.beforeFind * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before find function for. This can instead be a String that is the className of the subclass. * @param {Function} func The function to run before a find. This function can be async and should take just one parameter, {@link Parse.Cloud.BeforeFindRequest}. * @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.BeforeFindRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); triggers.addTrigger( triggers.Types.beforeFind, className, handler, Parse.applicationId, validationHandler ); }; /** * Registers an after find function. * * **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. * ``` * Parse.Cloud.afterFind('MyCustomClass', async (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.afterFind(Parse.User, async (request) => { * // code here * }, { ...validationObject }); *``` * * @method afterFind * @name Parse.Cloud.afterFind * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after find function for. This can instead be a String that is the className of the subclass. * @param {Function} func The function to run before a find. This function can be async and should take just one parameter, {@link Parse.Cloud.AfterFindRequest}. * @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.AfterFindRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.afterFind = function (parseClass, handler, validationHandler) { const className = getClassName(parseClass); triggers.addTrigger( triggers.Types.afterFind, className, handler, Parse.applicationId, 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) { triggers.addFileTrigger( triggers.Types.beforeSaveFile, handler, Parse.applicationId, 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) { triggers.addFileTrigger( triggers.Types.afterSaveFile, handler, Parse.applicationId, 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) { triggers.addFileTrigger( triggers.Types.beforeDeleteFile, handler, Parse.applicationId, 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 * @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) { triggers.addFileTrigger( triggers.Types.afterDeleteFile, handler, Parse.applicationId, validationHandler ); }; /** * Registers a before live query server connect function. * * **Available in Cloud Code only.** * * ``` * Parse.Cloud.beforeConnect(async (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.beforeConnect(async (request) => { * // code here * }, { ...validationObject }); *``` * * @method beforeConnect * @name Parse.Cloud.beforeConnect * @param {Function} func The function to before connection is made. This function can be async and should take just one parameter, {@link Parse.Cloud.ConnectTriggerRequest}. * @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.ConnectTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeConnect = function (handler, validationHandler) { triggers.addConnectTrigger( triggers.Types.beforeConnect, handler, Parse.applicationId, validationHandler ); }; /** * Registers a before live query subscription function. * * **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. * ``` * Parse.Cloud.beforeSubscribe('MyCustomClass', (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.beforeSubscribe(Parse.User, (request) => { * // code here * }, { ...validationObject }); *``` * * @method beforeSubscribe * @name Parse.Cloud.beforeSubscribe * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before subscription function for. This can instead be a String that is the className of the subclass. * @param {Function} func The function to run before a subscription. 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.beforeSubscribe = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); triggers.addTrigger( triggers.Types.beforeSubscribe, className, handler, Parse.applicationId, validationHandler ); }; ParseCloud.onLiveQueryEvent = function (handler) { triggers.addLiveQueryEventHandler(handler, Parse.applicationId); }; /** * Registers an after live query server event function. * * **Available in Cloud Code only.** * * ``` * Parse.Cloud.afterLiveQueryEvent('MyCustomClass', (request) => { * // code here * }, (request) => { * // validation code here * }); * * Parse.Cloud.afterLiveQueryEvent('MyCustomClass', (request) => { * // code here * }, { ...validationObject }); *``` * * @method afterLiveQueryEvent * @name Parse.Cloud.afterLiveQueryEvent * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after live query event function for. This can instead be a String that is the className of the subclass. * @param {Function} func The function to run after a live query event. This function can be async and should take one parameter, a {@link Parse.Cloud.LiveQueryEventTrigger}. * @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.LiveQueryEventTrigger}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.afterLiveQueryEvent = function ( parseClass, handler, validationHandler ) { const className = getClassName(parseClass); triggers.addTrigger( triggers.Types.afterEvent, className, handler, Parse.applicationId, validationHandler ); }; ParseCloud._removeAllHooks = () => { triggers._unregisterAll(); }; ParseCloud.useMasterKey = () => { // eslint-disable-next-line console.warn( 'Parse.Cloud.useMasterKey is deprecated (and has no effect anymore) on parse-server, please refer to the cloud code migration notes: http://docs.parseplatform.org/parse-server/guide/#master-key-must-be-passed-explicitly' ); }; ParseCloud.httpRequest = require('./httpRequest'); module.exports = ParseCloud; /** * @interface Parse.Cloud.TriggerRequest * @property {String} installationId If set, the installationId triggering the request. * @property {Boolean} master If true, means the master key was used. * @property {Parse.User} user If set, the user that made the request. * @property {Parse.Object} object The object triggering the hook. * @property {String} ip The IP address of the client making the request. * @property {Object} headers The original HTTP headers for the request. * @property {String} triggerName The name of the trigger (`beforeSave`, `afterSave`, ...) * @property {Object} log The current logger inside Parse Server. * @property {Parse.Object} original If set, the object, as currently stored. */ /** * @interface Parse.Cloud.FileTriggerRequest * @property {String} installationId If set, the installationId triggering the request. * @property {Boolean} master If true, means the master key was used. * @property {Parse.User} user If set, the user that made the request. * @property {Parse.File} file The file that triggered the hook. * @property {Integer} fileSize The size of the file in bytes. * @property {Integer} contentLength The value from Content-Length header * @property {String} ip The IP address of the client making the request. * @property {Object} headers The original HTTP headers for the request. * @property {String} triggerName The name of the trigger (`beforeSaveFile`, `afterSaveFile`) * @property {Object} log The current logger inside Parse Server. */ /** * @interface Parse.Cloud.ConnectTriggerRequest * @property {String} installationId If set, the installationId triggering the request. * @property {Boolean} useMasterKey If true, means the master key was used. * @property {Parse.User} user If set, the user that made the request. * @property {Integer} clients The number of clients connected. * @property {Integer} subscriptions The number of subscriptions connected. * @property {String} sessionToken If set, the session of the user that made the request. */ /** * @interface Parse.Cloud.LiveQueryEventTrigger * @property {String} installationId If set, the installationId triggering the request. * @property {Boolean} useMasterKey If true, means the master key was used. * @property {Parse.User} user If set, the user that made the request. * @property {String} sessionToken If set, the session of the user that made the request. * @property {String} event The live query event that triggered the request. * @property {Parse.Object} object The object triggering the hook. * @property {Parse.Object} original If set, the object, as currently stored. * @property {Integer} clients The number of clients connected. * @property {Integer} subscriptions The number of subscriptions connected. * @property {Boolean} sendEvent If the LiveQuery event should be sent to the client. Set to false to prevent LiveQuery from pushing to the client. */ /** * @interface Parse.Cloud.BeforeFindRequest * @property {String} installationId If set, the installationId triggering the request. * @property {Boolean} master If true, means the master key was used. * @property {Parse.User} user If set, the user that made the request. * @property {Parse.Query} query The query triggering the hook. * @property {String} ip The IP address of the client making the request. * @property {Object} headers The original HTTP headers for the request. * @property {String} triggerName The name of the trigger (`beforeSave`, `afterSave`, ...) * @property {Object} log The current logger inside Parse Server. * @property {Boolean} isGet wether the query a `get` or a `find` */ /** * @interface Parse.Cloud.AfterFindRequest * @property {String} installationId If set, the installationId triggering the request. * @property {Boolean} master If true, means the master key was used. * @property {Parse.User} user If set, the user that made the request. * @property {Parse.Query} query The query triggering the hook. * @property {Array} results The results the query yielded. * @property {String} ip The IP address of the client making the request. * @property {Object} headers The original HTTP headers for the request. * @property {String} triggerName The name of the trigger (`beforeSave`, `afterSave`, ...) * @property {Object} log The current logger inside Parse Server. */ /** * @interface Parse.Cloud.FunctionRequest * @property {String} installationId If set, the installationId triggering the request. * @property {Boolean} master If true, means the master key was used. * @property {Parse.User} user If set, the user that made the request. * @property {Object} params The params passed to the cloud function. */ /** * @interface Parse.Cloud.JobRequest * @property {Object} params The params passed to the background job. * @property {function} message If message is called with a string argument, will update the current message to be stored in the job status. */ /** * @interface Parse.Cloud.ValidatorObject * @property {Boolean} requireUser whether the cloud trigger requires a user. * @property {Boolean} requireMaster whether the cloud trigger requires a master key. * @property {Boolean} validateMasterKey whether the validator should run if masterKey is provided. Defaults to false. * * @property {Array|Object} requireUserKeys If set, keys required on request.user to make the request. * @property {String} requireUserKeys.field If requireUserKeys is an object, name of field to validate on request user * @property {Array|function|Any} requireUserKeys.field.options array of options that the field can be, function to validate field, or single value. Throw an error if value is invalid. * @property {String} requireUserKeys.field.error custom error message if field is invalid. * * @property {Object|Array} fields if an array of strings, validator will look for keys in request.params, and throw if not provided. If Object, fields to validate. If the trigger is a cloud function, `request.params` will be validated, otherwise `request.object`. * @property {String} fields.field name of field to validate. * @property {String} fields.field.type expected type of data for field. * @property {Boolean} fields.field.constant whether the field can be modified on the object. * @property {Any} fields.field.default default value if field is `null`, or initial value `constant` is `true`. * @property {Array|function|Any} fields.field.options array of options that the field can be, function to validate field, or single value. Throw an error if value is invalid. * @property {String} fields.field.error custom error message if field is invalid. */