Skip to content

Commit

Permalink
tests working action
Browse files Browse the repository at this point in the history
  • Loading branch information
mpresecan committed Jun 13, 2024
1 parent 2cc9091 commit f716b42
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14.x'
node-version: '16.x'
registry-url: 'https://registry.npmjs.org'
- run: yarn install
- run: yarn build
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Payload Action Scheduler (in development)
# Payload Action Scheduler (in alpha)
Add scheduled tasks to your Payload app effortlessly. Whether you need to postpone a task to a specific future time, set up periodic tasks, or offload tasks to run in the background, Payload Action Scheduler has you covered. With this plugin, you can monitor your task queues, see execution results, and track execution timestamps seamlessly.

![scheduled-actions-view.png](readme/scheduled-actions-view.png)
Expand Down
26 changes: 25 additions & 1 deletion dev/src/payload.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,38 @@ export default buildConfig({
plugins: [actionScheduler({
enabled: true,
actions: [
{
endpoint: 'test',
async handler(payload, args) {
console.log('Running test action', args, typeof args)
return 'some message in return';
}
},
{
endpoint: 'test2',
handler: async (payload, args) => {
console.log('Running test2 action', args, typeof args)
}
},
{
endpoint: 'long-running-action',
handler: async (payload, args) => {
console.log('Running long-waiting-action action', args, typeof args)
await new Promise(resolve => setTimeout(resolve, 25000));
}
}
]
],
errorHooks: [
async ({ payload, action, message, code }) => {
console.error('ERROR LOG 1:', { action, message, code })
},
async ({ payload, action, message, code }) => {
console.error('ERROR LOG 2:', { action, message, code })
}
],
debug: true,
timeoutSeconds: 10,
runtime: 'serverless',
})],
graphQL: {
disable: true,
Expand Down
11 changes: 6 additions & 5 deletions src/collections/ScheduledActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
RecurringCell,
} from '../components/Cells'
import {parseExpression} from 'cron-parser'
import {AddActionType, PluginTypes} from "../types";
import {AddActionType, PluginTypes, ScheduledAction} from "../types";
import {MAX_NODE_TIMEOUT_SECONDS, pluginDefaults} from "../defaults";
import {sortObjectKeys} from "../helpers";
import {
Expand All @@ -22,7 +22,7 @@ import {
import {stringifyDiff} from "../helpers/time-server";
import {GeneratedTypes} from "payload";

export const SCHEDULED_ACTIONS_COLLECTION_SLUG = 'scheduled-actions' // do not put in config to change the collection slug
export const SCHEDULED_ACTIONS_COLLECTION_SLUG = 'scheduled-actions' as const // do not put in config to change the collection slug
export const SCHEDULED_ACTIONS_ENDPOINT = '/run'

// export const RECURRING_INTERVAL_SECONDS = 60 // not needed
Expand All @@ -40,6 +40,7 @@ export const ScheduledActions = (pluginConfig: PluginTypes) : CollectionConfig =
timeoutSeconds: timeoutSecondsConst,
errorHooks,
apiURL,
collectionGroup,
} = {
...pluginDefaults,
...pluginConfig
Expand All @@ -59,7 +60,7 @@ export const ScheduledActions = (pluginConfig: PluginTypes) : CollectionConfig =
slug: SCHEDULED_ACTIONS_COLLECTION_SLUG,
access: {
create: createAccess,
delete: () => false,
delete: () => true,
update: () => false,
read: readAccess,
},
Expand All @@ -76,6 +77,7 @@ export const ScheduledActions = (pluginConfig: PluginTypes) : CollectionConfig =
components: {
BeforeListTable: [BeforeScheduledActionsList],
},
group: collectionGroup
},
disableDuplicate: true,
hooks: {
Expand Down Expand Up @@ -107,7 +109,6 @@ export const ScheduledActions = (pluginConfig: PluginTypes) : CollectionConfig =
method: 'get',
async handler(request) {
const {payload} = request

const startTime = new Date()
let duration: number
let totalDocs = 0
Expand All @@ -130,7 +131,7 @@ export const ScheduledActions = (pluginConfig: PluginTypes) : CollectionConfig =

successCount = await processActionsQueue({
payload: payload,
actions: results.docs as GeneratedTypes["collections"]["scheduled-actions"][],
actions: results.docs as ScheduledAction[],
actionHandlers: actions!,
timeoutSeconds: timeoutSeconds,
errorHooks: errorHooks!,
Expand Down
1 change: 1 addition & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export const pluginDefaults : PluginTypes = {
errorHooks: [],
enabled: true,
apiURL: '/api',
collectionGroup: 'Collections'
}
16 changes: 0 additions & 16 deletions src/newCollection.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const actionScheduler =
config.collections = [
...(config.collections || []),
// Add additional collections here
ScheduledActions(config),
ScheduledActions(pluginOptions),
]

config.globals = [
Expand Down
15 changes: 11 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,17 @@ export interface PluginTypes {
* @default '/api'
*/
apiURL?: string,

/**
* Collection group admin dashboard for the scheduled actions
* @default 'Collections'
*/
collectionGroup?: string,
}

export interface ErrorLogFunctionArgs{
payload: Payload,
action: GeneratedTypes["collections"]["scheduled-actions"],
action: ScheduledAction,
message: string,
code?: number,
}
Expand All @@ -75,10 +81,11 @@ export type AddActionType = {
scheduledAt?: Date,
cronExpression?: string,
priority?: number,
payload: Payload
}

export type GetActionType = AddActionType & {
status?: GeneratedTypes["collections"]["scheduled-actions"]['status'],
export type GetActionType = Omit<AddActionType, 'payload'> & {
status?: ScheduledAction['status'],
}

export type ActionDefinition = {
Expand All @@ -88,7 +95,7 @@ export type ActionDefinition = {

export interface RunActionArgs {
payload: BasePayload<GeneratedTypes>;
action: GeneratedTypes["collections"]["scheduled-actions"];
action: ScheduledAction;
timeoutSeconds: number;
actionHandlers: ActionDefinition[];
errorHooks: ((args: ErrorLogFunctionArgs) => Promise<void>)[];
Expand Down
33 changes: 14 additions & 19 deletions src/utilities/scheduled-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import {
AddActionType,
GetActionType,
ErrorLogFunctionArgs,
RunActionArgs, TimeoutError
RunActionArgs, TimeoutError, ScheduledAction
} from "../types";
import {parseExpression} from "cron-parser";
import {SCHEDULED_ACTIONS_COLLECTION_SLUG} from "../collections/ScheduledActions";
import {normalizeJSONString, sortObjectKeys} from "../helpers";
import {PaginatedDocs} from "payload/database";
import {stringifyDiff} from "../helpers/time-server";
import {generateSignature} from "./security";

Expand All @@ -28,7 +27,7 @@ export const addAction = async (props: AddActionType) => {
log,
} = action

const payload = await getPayload({config: configPromise})
const {payload} = props;

const isScheduled = await isActionScheduled(payload, endpoint, scheduledDateTime, 'pending')
if (isScheduled)
Expand Down Expand Up @@ -106,7 +105,7 @@ export const constructNewAction = (props: AddActionType) => {
}


export const isActionScheduled = async (payload: BasePayload<GeneratedTypes>, endpoint: string, scheduledAt?: Date | string, status?: GeneratedTypes["collections"]["scheduled-actions"]['status']) => {
export const isActionScheduled = async (payload: BasePayload<GeneratedTypes>, endpoint: string, scheduledAt?: Date | string, status?: ScheduledAction['status']) => {

if (typeof scheduledAt === 'string') {
scheduledAt = new Date(scheduledAt)
Expand Down Expand Up @@ -206,7 +205,7 @@ export const processActionsQueue = async ({
errorHooks,
apiURL
}: Omit<RunActionArgs, 'action'> & {
actions: GeneratedTypes["collections"]["scheduled-actions"][]
actions: ScheduledAction[]
}) => {
if (actions.length === 0) {
return 0
Expand Down Expand Up @@ -240,7 +239,7 @@ export const processActionsQueue = async ({
}
}

export const updateActionsToRunningStatus = async (payload: BasePayload<GeneratedTypes>, actions: GeneratedTypes["collections"]["scheduled-actions"][]) => {
export const updateActionsToRunningStatus = async (payload: BasePayload<GeneratedTypes>, actions: ScheduledAction[]) => {
await payload.update({
collection: SCHEDULED_ACTIONS_COLLECTION_SLUG,
where: {
Expand Down Expand Up @@ -268,13 +267,11 @@ export const runAction = async ({

// first determine the action
let actionPromise: Promise<Response>
// @ts-ignore
if (action.endpoint.startsWith('@')) {

let registeredAction = actionHandlers.find(a => a.endpoint === action.endpoint)
// @ts-ignore

if (!registeredAction && action.endpoint.startsWith('@')) {
// @ts-ignore
registeredAction = actionHandlers.find(a => a.endpoint === action.endpoint.slice(1))
}

Expand All @@ -290,11 +287,9 @@ export const runAction = async ({
return Promise.reject(new Error(`Action ${action.endpoint} is not a function`))
}

// @ts-ignore
actionPromise = registeredAction.handler(payload, action.args)

} else {
// @ts-ignore
const endPoint: string = action.endpoint.startsWith('http') ? action.endpoint : `${apiURL}${action.endpoint}`

actionPromise = fetch(endPoint, {
Expand Down Expand Up @@ -327,7 +322,7 @@ export const runAction = async ({
throw error
})

let status: GeneratedTypes["collections"]["scheduled-actions"]['status'] = 'failed'
let status: ScheduledAction['status'] = 'failed'
let message: string = 'Unknown error'
let code: number = 500

Expand Down Expand Up @@ -371,7 +366,7 @@ export const runAction = async ({
}
}

const logStartAction = (action: GeneratedTypes["collections"]["scheduled-actions"]) => {
const logStartAction = (action: ScheduledAction) => {
// @ts-ignore
action.log?.push({
date: new Date().toISOString(),
Expand All @@ -382,8 +377,8 @@ const logStartAction = (action: GeneratedTypes["collections"]["scheduled-actions
const actionCleanup = async (
payload: BasePayload<GeneratedTypes>,
errorHooks: ((args: ErrorLogFunctionArgs) => Promise<void>)[],
action: GeneratedTypes["collections"]["scheduled-actions"],
status: GeneratedTypes["collections"]["scheduled-actions"]['status'],
action: ScheduledAction,
status: ScheduledAction['status'],
message: string,
code?: number,
) => {
Expand All @@ -402,8 +397,8 @@ const actionCleanup = async (

export const logAction = async (
payload: BasePayload<GeneratedTypes>,
action: GeneratedTypes["collections"]["scheduled-actions"],
status: GeneratedTypes["collections"]["scheduled-actions"]["status"],
action: ScheduledAction,
status: ScheduledAction["status"],
message: string,
code?: number,
calculateTimeDiff = false
Expand Down Expand Up @@ -454,7 +449,7 @@ export const logAction = async (
const executeErrorLog = async (
payload: BasePayload<GeneratedTypes>,
errorHooks: ((args: ErrorLogFunctionArgs) => Promise<void>)[],
action: GeneratedTypes["collections"]["scheduled-actions"],
action: ScheduledAction,
message: string,
code?: number
) => {
Expand All @@ -470,7 +465,7 @@ const executeErrorLog = async (
}


const rescheduleAction = async (payload: BasePayload<GeneratedTypes>, action: GeneratedTypes["collections"]["scheduled-actions"]) => {
const rescheduleAction = async (payload: BasePayload<GeneratedTypes>, action: ScheduledAction) => {

if (!action.cronExpression) {
throw new Error('Cannot reschedule an action without a cron expression')
Expand Down

0 comments on commit f716b42

Please sign in to comment.