Triggers
Concept
Contains the .
Folder structure
├── triggers
│ ├── beforeSave.js
│ ├── afterSave.js
│ ├── beforeDelete.js
│ ├── afterDelete.js
│ ├── beforeFind.js
│ ├── afterFind.js
- The index.js (or triggers.js) exports the triggers functions
Variant entry points
None
Usage
Default argument for all triggers
Argument | Description | Type | Default value | |
---|---|---|---|---|
request | The Parse Server request | Object | {} |
Request content: | Argument | Description | Type | Default value | |:--|:--|:--|:--|:--| | object | The Parse Server object | Object | {} | | original | The Parse Server object before it's modified | Object | {} | | context | The Parse Server context | Object | {} |
Before save
export default async (props) => {
const { request } = props
const { object } = request
}
After save
export default async (props) => {
const { request } = props
const { object } = request
}
Before delete
export default async (props) => {
const { request } = props
const { object } = request
}
After delete
export default async (props) => {
const { request } = props
const { object } = request
}
Example
The disposablechildrenable protocol
import _ from 'underscore'
//afterSave.js
export default async ({ request }) => {
const { object, original, context } = request
if (!object || !object.disposableChildren) {
return
}
const { dirtyKeys } = context
if (!dirtyKeys || !dirtyKeys.length) {
return
}
const items = object.disposableChildren()
if (!items || !items.length) {
return
}
const candidates = _.intersection(items, dirtyKeys)
if (!candidates || !candidates.length) {
return
}
await Promise.all(candidates.map(key => handleItem({ object, original, key })))
}
const handleItem = async ({ object, original, key }) => {
if (!original) {
return
}
const previous = original.get(key)
const current = object.get(key)
if (Array.isArray(previous) || Array.isArray(current)) {
return handleArray({ previous, current })
}
const isFile = isFieldFile(previous)
| isFieldFile(current)
if (!isFile) {
return handleItemAsObject({
previous,
current
})
}
return handleItemAsFile({
previous,
current
})
}
const handleArray = async ({ previous, current }) => {
const objects = missingObjectsBetweenArrays(previous, current)
//TODO: objects === 0 when [files]
if (!objects || !objects.length) {
return
}
if (isFieldFile(objects[0])) {
await Promise.all(objects.map(destroyItemAsFile))
} else {
await Promise.all(objects.map(destroyItemAsObject))
}
}
const handleItemAsObject = async ({ previous, current }) => {
const objects = missingObjectsBetweenArrays([previous], [current])
//TODO: objects === 0 when [files]
if (!objects || !objects.length) {
return
}
await Promise.all(objects.map(destroyItemAsObject))
}
const handleItemAsFile = async ({ previous, current }) => {
if (!previous) {
return
}
if (!current) {
return destroyItemAsFile(previous)
}
if (previous.name() !== current.name()) {
return destroyItemAsFile(previous)
}
}
const missingObjectsBetweenArrays = (a, b) => {
if (!b || !b.length) {
return a
}
if (!a || !a.length) {
return []
}
let _a = a.filter(i => i)
let _b = b.filter(i => i)
const isFile = isFieldFile(_b[0])
if (isFile) {
return missingObjectsBetweenArraysFiles(_a, _b)
}
else {
return missingObjectsBetweenArraysObject(_a, _b)
}
}
const missingObjectsBetweenArraysFiles = (a, b) => {
const bUrls = b.map(i => i.url())
return a.map(i => {
//TODO: handle files
if (!bUrls.includes(i.url())) {
return i
}
return null
}).filter(a => a)
}
const missingObjectsBetweenArraysObject = (a, b) => {
const bIds = b.map(i => i.id)
return a.map(i => {
//TODO: handle files
if (!bIds.includes(i.id)) {
return i
}
return null
}).filter(a => a)
}
const isFieldFile = (item) => {
if (!item) {
return false
}
return (item._name && item._url)
}
const destroyItemAsObject = async i => {
try {
i.isDataAvailable && !i.isDataAvailable() && i.fetch && (await i.fetch({ useMasterKey: true })) // The check will skip files
await i.destroy({ useMasterKey: true })
} catch (e) {
console.error('Protocol > dependentChildren > destroyItem', e.message)
}
}
const destroyItemAsFile = async i => {
try {
await i.destroy({ useMasterKey: true })
} catch (e) {
console.error('Protocol > dependentChildren > destroyItem', e.message)
}
}