feat(plugin-workflow): add date range options to system variables (#4728)

* feat(plugin-workflow): add date range options to system variable in workflow plugin

* feat(plugin-workflow): fix according to code review

* feat(plugin-workflow): add more functions

* feat(plugin-workflow): fix according to code review

* feat(plugin-workflow): add test

* feat(plugin-workflow): support india timezone

* feat(plugin-workflow): fix test

* feat(plugin-workflow): add ts type

* feat(plugin-workflow): fix

* feat(plugin-workflow): fix
This commit is contained in:
shz 2024-06-30 16:30:31 +08:00 committed by GitHub
parent 86a56ef7cc
commit 512557f80a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 842 additions and 4 deletions

View File

@ -260,9 +260,15 @@ export function utc2unit(options: Utc2unitOptions) {
const r = fn[unit]?.();
return timezone ? r + timezone : r;
}
type ToUnitParams = {
now?: any;
timezone?: string | number;
field?: {
timezone?: string | number;
};
};
export const toUnit = (unit, offset?: number) => {
return ({ now, timezone, field }) => {
return ({ now, timezone, field }: ToUnitParams) => {
if (field?.timezone) {
timezone = field?.timezone;
}
@ -271,7 +277,7 @@ export const toUnit = (unit, offset?: number) => {
};
const toDays = (offset: number) => {
return ({ now, timezone, field }) => {
return ({ now, timezone, field }: ToUnitParams) => {
if (field?.timezone) {
timezone = field?.timezone;
}

View File

@ -8,6 +8,7 @@
*/
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { uniqBy } from 'lodash';
import { Variable, parseCollectionName, useApp, useCompile, usePlugin } from '@nocobase/client';
@ -53,6 +54,114 @@ export type UseVariableOptions = {
export const defaultFieldNames = { label: 'label', value: 'value', children: 'children' } as const;
const getDateOptions = (t) => [
{
key: 'yesterday',
value: 'yesterday',
label: t('Yesterday'),
},
{
key: 'today',
value: 'today',
label: t('Today'),
},
{
key: 'tomorrow',
value: 'tomorrow',
label: t('Tomorrow'),
},
{
key: 'lastWeek',
value: 'lastWeek',
label: t('Last week'),
},
{
key: 'thisWeek',
value: 'thisWeek',
label: t('This week'),
},
{
key: 'nextWeek',
value: 'nextWeek',
label: t('Next week'),
},
{
key: 'lastMonth',
value: 'lastMonth',
label: t('Last month'),
},
{
key: 'thisMonth',
value: 'thisMonth',
label: t('This month'),
},
{
key: 'nextMonth',
value: 'nextMonth',
label: t('Next month'),
},
{
key: 'lastQuarter',
value: 'lastQuarter',
label: t('Last quarter'),
},
{
key: 'thisQuarter',
value: 'thisQuarter',
label: t('This quarter'),
},
{
key: 'nextQuarter',
value: 'nextQuarter',
label: t('Next quarter'),
},
{
key: 'lastYear',
value: 'lastYear',
label: t('Last year'),
},
{
key: 'thisYear',
value: 'thisYear',
label: t('This year'),
},
{
key: 'nextYear',
value: 'nextYear',
label: t('Next year'),
},
{
key: 'last7Days',
value: 'last7Days',
label: t('Last 7 days'),
},
{
key: 'next7Days',
value: 'next7Days',
label: t('Next 7 days'),
},
{
key: 'last30Days',
value: 'last30Days',
label: t('Last 30 days'),
},
{
key: 'next30Days',
value: 'next30Days',
label: t('Next 30 days'),
},
{
key: 'last90Days',
value: 'last90Days',
label: t('Last 90 days'),
},
{
key: 'next90Days',
value: 'next90Days',
label: t('Next 90 days'),
},
];
export const nodesOptions = {
label: `{{t("Node result", { ns: "${NAMESPACE}" })}}`,
value: '$jobsMapByNodeKey',
@ -113,6 +222,7 @@ export const systemOptions = {
label: `{{t("System variables", { ns: "${NAMESPACE}" })}}`,
value: '$system',
useOptions({ types, fieldNames = defaultFieldNames }: UseVariableOptions) {
const { t } = useTranslation();
return [
...(!types || types.includes('date')
? [
@ -121,6 +231,12 @@ export const systemOptions = {
[fieldNames.label]: lang('System time'),
[fieldNames.value]: 'now',
},
{
key: 'dateRange',
[fieldNames.label]: lang('Date range'),
[fieldNames.value]: 'dateRange',
children: getDateOptions(t),
},
]
: []),
];

View File

@ -87,6 +87,7 @@
"System variables": "시스템 변수",
"System time": "시스템 시간",
"Date variables": "날짜 변수",
"Date range": "날짜 범위",
"Executed at": "실행된 시각",
"Queueing": "대기 중",
"On going": "진행 중",

View File

@ -104,6 +104,7 @@
"System variables": "系统变量",
"System time": "系统时间",
"Date variables": "日期变量",
"Date range": "日期范围",
"Executed at": "执行于",
"Queueing": "队列中",

View File

@ -11,6 +11,7 @@ import { Model, Transaction, Transactionable } from '@nocobase/database';
import { appendArrayColumn } from '@nocobase/evaluators';
import { Logger } from '@nocobase/logger';
import { parse } from '@nocobase/utils';
import set from 'lodash/set';
import type Plugin from './Plugin';
import { EXECUTION_STATUS, JOB_STATUS } from './constants';
import { Runner } from './instructions';
@ -381,7 +382,7 @@ export default class Processor {
node,
};
for (const [name, fn] of this.options.plugin.functions.getEntities()) {
systemFns[name] = fn.bind(scope);
set(systemFns, name, fn.bind(scope));
}
const $scopes = {};

View File

@ -0,0 +1,628 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import dayjs from 'dayjs';
import { Application } from '@nocobase/server';
import Database from '@nocobase/database';
import { parse } from '@nocobase/utils';
import { dateRangeFns } from '@nocobase/plugin-workflow';
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
let last2days;
let yesterday;
let startOfYesterday;
let endOfYesterday;
let today;
let startOfToday;
let endOfToday;
let tomorrow;
let startOfTomorrow;
let endOfTomorrow;
let theDayAfterTomorrow;
let lastWeek;
let lastWeekFirstDay;
let lastWeekLastDay;
let thisWeekFirstDay;
let thisWeekLastDay;
let nextWeek;
let nextWeekFirstDay;
let nextWeekLastDay;
let lastMonth;
let lastMonthFirstDay;
let lastMonthLastDay;
let thisMonthFirstDay;
let thisMonthLastDay;
let nextMonth;
let nextMonthFirstDay;
let nextMonthLastDay;
let lastQuarter;
let lastQuarterFirstDay;
let lastQuarterLastDay;
let thisQuarterFirstDay;
let thisQuarterLastDay;
let nextQuarter;
let nextQuarterFirstDay;
let nextQuarterLastDay;
let lastYear;
let lastYearFirstDay;
let lastYearLastDay;
let thisYearFirstDay;
let thisYearLastDay;
let nextYear;
let nextYearFirstDay;
let nextYearLastDay;
describe('workflow > functions > system variable', () => {
let app: Application;
let db: Database;
let PostCollection;
let PostRepo;
let TagModel;
let CommentRepo;
let WorkflowModel;
let workflow;
beforeEach(async () => {
app = await getApp();
db = app.db;
PostCollection = db.getCollection('posts');
PostRepo = PostCollection.repository;
last2days = dayjs().tz(timezone).subtract(2, 'day');
yesterday = dayjs().tz(timezone).subtract(1, 'day');
startOfYesterday = yesterday.clone().startOf('day');
endOfYesterday = yesterday.clone().endOf('day');
today = dayjs().tz(timezone);
startOfToday = today.clone().startOf('day');
endOfToday = today.clone().endOf('day');
tomorrow = dayjs().tz(timezone).add(1, 'day');
startOfTomorrow = tomorrow.clone().startOf('day');
endOfTomorrow = tomorrow.clone().endOf('day');
theDayAfterTomorrow = dayjs().tz(timezone).add(2, 'day');
lastWeek = dayjs().tz(timezone).subtract(1, 'week');
lastWeekFirstDay = lastWeek.clone().startOf('week');
lastWeekLastDay = lastWeek.clone().endOf('week');
thisWeekFirstDay = today.clone().tz(timezone).startOf('week');
thisWeekLastDay = today.clone().endOf('week');
nextWeek = dayjs().tz(timezone).add(1, 'week');
nextWeekFirstDay = nextWeek.clone().startOf('week');
nextWeekLastDay = nextWeek.clone().endOf('week');
lastMonth = dayjs().tz(timezone).subtract(1, 'month');
lastMonthFirstDay = lastMonth.clone().startOf('month');
lastMonthLastDay = lastMonth.clone().endOf('month');
thisMonthFirstDay = today.clone().startOf('month');
thisMonthLastDay = today.clone().endOf('month');
nextMonth = dayjs().tz(timezone).add(1, 'month');
nextMonthFirstDay = nextMonth.clone().startOf('month');
nextMonthLastDay = nextMonth.clone().endOf('month');
lastQuarter = dayjs().tz(timezone).subtract(1, 'quarter');
lastQuarterFirstDay = lastQuarter.clone().startOf('quarter');
lastQuarterLastDay = lastQuarter.clone().endOf('quarter');
thisQuarterFirstDay = today.clone().startOf('quarter');
thisQuarterLastDay = today.clone().endOf('quarter');
nextQuarter = dayjs().tz(timezone).add(1, 'quarter');
nextQuarterFirstDay = nextQuarter.clone().startOf('quarter');
nextQuarterLastDay = nextQuarter.clone().endOf('quarter');
lastYear = dayjs().tz(timezone).subtract(1, 'year');
lastYearFirstDay = lastYear.clone().startOf('year');
lastYearLastDay = lastYear.clone().endOf('year');
thisYearFirstDay = today.clone().startOf('year');
thisYearLastDay = today.clone().endOf('year');
nextYear = dayjs().tz(timezone).add(1, 'year');
nextYearFirstDay = nextYear.clone().startOf('year');
nextYearLastDay = nextYear.clone().endOf('year');
});
afterEach(() => app.destroy());
describe('system variable should', () => {
it('filter yesterday record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.yesterday}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'yesterday', updatedAt: yesterday.toDate() },
{ title: 'start of yesterday', updatedAt: startOfYesterday.toDate() },
{
title: 'the time before start of yesterday 1s',
updatedAt: startOfYesterday.clone().subtract(1, 's').toDate(),
},
{ title: 'end of yesterday', updatedAt: endOfYesterday.toDate() },
{ title: 'the time after end of yesterday 1s', updatedAt: endOfYesterday.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter today record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.today}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'today', updatedAt: today.toDate() },
{ title: 'start of today', updatedAt: startOfToday.toDate() },
{ title: 'the time before start of today 1s', updatedAt: startOfToday.clone().subtract(1, 's').toDate() },
{ title: 'end of today', updatedAt: endOfToday.toDate() },
{ title: 'the time after end of today 1s', updatedAt: endOfToday.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter tomorrow record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.tomorrow}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'tomorrow', updatedAt: tomorrow.toDate() },
{ title: 'start of tomorrow', updatedAt: startOfTomorrow.toDate() },
{
title: 'the time before start of tomorrow 1s',
updatedAt: startOfTomorrow.clone().subtract(1, 's').toDate(),
},
{ title: 'end of tomorrow', updatedAt: endOfTomorrow.toDate() },
{ title: 'the time after end of tomorrow 1s', updatedAt: endOfTomorrow.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter last week record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.lastWeek}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'last week', updatedAt: lastWeek.toDate() },
{ title: 'start of last week', updatedAt: lastWeekFirstDay.toDate() },
{
title: 'the time before start of last week 1s',
updatedAt: lastWeekFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of last week', updatedAt: lastWeekLastDay.toDate() },
{ title: 'the time after end of last week 1s', updatedAt: lastWeekLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter this week record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.thisWeek}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'this week', updatedAt: today.toDate() },
{ title: 'start of this week', updatedAt: thisWeekFirstDay.toDate() },
{
title: 'the time before start of this week 1s',
updatedAt: thisWeekFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of this week', updatedAt: thisWeekLastDay.toDate() },
{ title: 'the time after end of this week 1s', updatedAt: thisWeekLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter next week record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.nextWeek}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'next week', updatedAt: nextWeek.toDate() },
{ title: 'start of next week', updatedAt: nextWeekFirstDay.toDate() },
{
title: 'the time before start of next week 1s',
updatedAt: nextWeekFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of next week', updatedAt: nextWeekLastDay.toDate() },
{ title: 'the time after end of next week 1s', updatedAt: nextWeekLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter last month record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.lastMonth}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'last month', updatedAt: lastMonth.toDate() },
{ title: 'start of last month', updatedAt: lastMonthFirstDay.toDate() },
{
title: 'the time before start of last month 1s',
updatedAt: lastMonthFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of last month', updatedAt: lastMonthLastDay.toDate() },
{ title: 'the time after end of last month 1s', updatedAt: lastMonthLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter this month record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.thisMonth}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'this month', updatedAt: today.toDate() },
{ title: 'start of this month', updatedAt: thisMonthFirstDay.toDate() },
{
title: 'the time before start of this month 1s',
updatedAt: thisMonthFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of this month', updatedAt: thisMonthLastDay.toDate() },
{ title: 'the time after end of this month 1s', updatedAt: thisMonthLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter next month record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.nextMonth}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'next month', updatedAt: nextMonth.toDate() },
{ title: 'start of next month', updatedAt: nextMonthFirstDay.toDate() },
{
title: 'the time before start of next month 1s',
updatedAt: nextMonthFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of next month', updatedAt: nextMonthLastDay.toDate() },
{ title: 'the time after end of next month 1s', updatedAt: nextMonthLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter last quarter record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.lastQuarter}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'last quarter', updatedAt: lastQuarter.toDate() },
{ title: 'start of last quarter', updatedAt: lastQuarterFirstDay.toDate() },
{
title: 'the time before start of last quarter 1s',
updatedAt: lastQuarterFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of last quarter', updatedAt: lastQuarterLastDay.toDate() },
{
title: 'the time after end of last quarter 1s',
updatedAt: lastQuarterLastDay.clone().add(1, 's').toDate(),
},
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter this quarter record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.thisQuarter}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'this quarter', updatedAt: today.toDate() },
{ title: 'start of this quarter', updatedAt: thisQuarterFirstDay.toDate() },
{
title: 'the time before start of this quarter 1s',
updatedAt: thisQuarterFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of this quarter', updatedAt: thisQuarterLastDay.toDate() },
{
title: 'the time after end of this quarter 1s',
updatedAt: thisQuarterLastDay.clone().add(1, 's').toDate(),
},
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter next quarter record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.nextQuarter}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'next quarter', updatedAt: nextQuarter.toDate() },
{ title: 'start of next quarter', updatedAt: nextQuarterFirstDay.toDate() },
{
title: 'the time before start of next quarter 1s',
updatedAt: nextQuarterFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of next quarter', updatedAt: nextQuarterLastDay.toDate() },
{
title: 'the time after end of next quarter 1s',
updatedAt: nextQuarterLastDay.clone().add(1, 's').toDate(),
},
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter last year record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.lastYear}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'last year', updatedAt: lastYear.toDate() },
{ title: 'start of last year', updatedAt: lastYearFirstDay.toDate() },
{
title: 'the time before start of last year 1s',
updatedAt: lastYearFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of last year', updatedAt: lastYearLastDay.toDate() },
{ title: 'the time after end of last year 1s', updatedAt: lastYearLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[0].title);
expect(result[1].title).toBe(posts[1].title);
expect(result[2].title).toBe(posts[3].title);
});
it('filter this year record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.thisYear}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'this year', updatedAt: today.toDate() },
{ title: 'start of this year', updatedAt: thisYearFirstDay.toDate() },
{
title: 'the time before start of this year 1s',
updatedAt: thisYearFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of this year', updatedAt: thisYearLastDay.toDate() },
{ title: 'the time after end of this year 1s', updatedAt: thisYearLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
it('filter next year record', async () => {
const filter = parse({
updatedAt: {
$dateOn: '{{$system.dateRange.nextYear}}',
},
})({
$system: { dateRange: dateRangeFns },
});
const posts = await PostRepo.createMany({
records: [
{ title: 'a year ago', updatedAt: lastYear.toDate() },
{ title: 'next year', updatedAt: nextYear.toDate() },
{ title: 'start of next year', updatedAt: nextYearFirstDay.toDate() },
{
title: 'the time before start of next year 1s',
updatedAt: nextYearFirstDay.clone().subtract(1, 's').toDate(),
},
{ title: 'end of next year', updatedAt: nextYearLastDay.toDate() },
{ title: 'the time after end of next year 1s', updatedAt: nextYearLastDay.clone().add(1, 's').toDate() },
],
silent: true,
});
await sleep(500);
const result = await PostRepo.find({ filter });
expect(result.length).toBe(3);
expect(result[0].title).toBe(posts[1].title);
expect(result[1].title).toBe(posts[2].title);
expect(result[2].title).toBe(posts[4].title);
});
});
});

View File

@ -7,18 +7,102 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { utc2unit, getDateVars } from '@nocobase/utils';
import dayjs, { Dayjs } from 'dayjs';
import Plugin from '..';
import type { ExecutionModel, FlowNodeModel } from '../types';
export type CustomFunction = (this: { execution: ExecutionModel; node?: FlowNodeModel }) => any;
function getTimezone() {
const offset = new Date().getTimezoneOffset();
const hours = String(Math.floor(Math.abs(offset) / 60)).padStart(2, '0');
const minutes = String(Math.abs(offset) % 60).padStart(2, '0');
const sign = offset <= 0 ? '+' : '-';
return `${sign}${hours}:${minutes}`;
}
const getRangeByDay = (offset: number) =>
utc2unit({ now: new Date(), unit: 'day', offset: offset, timezone: getTimezone() });
const getOffsetFromMS = (start, end) => Math.floor((end - start) / 1000 / 60 / 60 / 24);
function now() {
return new Date();
}
const dateVars = getDateVars();
export const dateRangeFns = {
yesterday() {
return dateVars.yesterday({ now: new Date(), timezone: getTimezone() });
},
today() {
return dateVars.today({ now: new Date(), timezone: getTimezone() });
},
tomorrow() {
return dateVars.tomorrow({ now: new Date(), timezone: getTimezone() });
},
lastWeek() {
return dateVars.lastWeek({ now: new Date(), timezone: getTimezone() });
},
thisWeek() {
return dateVars.thisWeek({ now: new Date(), timezone: getTimezone() });
},
nextWeek() {
return dateVars.nextWeek({ now: new Date(), timezone: getTimezone() });
},
lastMonth() {
return dateVars.lastMonth({ now: new Date(), timezone: getTimezone() });
},
thisMonth() {
return dateVars.thisMonth({ now: new Date(), timezone: getTimezone() });
},
nextMonth() {
return dateVars.nextMonth({ now: new Date(), timezone: getTimezone() });
},
lastQuarter() {
return dateVars.lastQuarter({ now: new Date(), timezone: getTimezone() });
},
thisQuarter() {
return dateVars.thisQuarter({ now: new Date(), timezone: getTimezone() });
},
nextQuarter() {
return dateVars.nextQuarter({ now: new Date(), timezone: getTimezone() });
},
lastYear() {
return dateVars.lastYear({ now: new Date(), timezone: getTimezone() });
},
thisYear() {
return dateVars.thisYear({ now: new Date(), timezone: getTimezone() });
},
nextYear() {
return dateVars.nextYear({ now: new Date(), timezone: getTimezone() });
},
last7Days() {
return dateVars.last7Days({ now: new Date(), timezone: getTimezone() });
},
next7Days() {
return dateVars.next7Days({ now: new Date(), timezone: getTimezone() });
},
last30Days() {
return dateVars.last30Days({ now: new Date(), timezone: getTimezone() });
},
next30Days() {
return dateVars.next30Days({ now: new Date(), timezone: getTimezone() });
},
last90Days() {
return dateVars.last90Days({ now: new Date(), timezone: getTimezone() });
},
next90Days() {
return dateVars.next90Days({ now: new Date(), timezone: getTimezone() });
},
};
export default function ({ functions }: Plugin, more: { [key: string]: CustomFunction } = {}) {
functions.register('now', now);
Object.keys(dateRangeFns).forEach((key) => {
functions.register(`dateRange.${key}`, dateRangeFns[key]);
});
for (const [name, fn] of Object.entries(more)) {
functions.register(name, fn);
}

View File

@ -10,6 +10,7 @@
export * from './utils';
export * from './constants';
export * from './instructions';
export * from './functions';
export { Trigger } from './triggers';
export { default as Processor } from './Processor';
export { default } from './Plugin';