|
|
|
@ -6,8 +6,8 @@ import { Logger } from '@nestjs/common';
|
|
|
|
|
import dayjs from 'dayjs'; |
|
|
|
|
import { isDateMonthFormat, UITypes } from 'nocodb-sdk'; |
|
|
|
|
import NcPluginMgrv2 from './NcPluginMgrv2'; |
|
|
|
|
import type { Column, FormView, Hook, Model, View } from '~/models'; |
|
|
|
|
import type { HookLogType } from 'nocodb-sdk'; |
|
|
|
|
import type { Column, FormView, Hook, Model, View } from '~/models'; |
|
|
|
|
import { Filter, HookLog, Source } from '~/models'; |
|
|
|
|
|
|
|
|
|
Handlebars.registerHelper('json', function (context) { |
|
|
|
@ -39,13 +39,124 @@ export async function validateCondition(
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let isValid = true; |
|
|
|
|
let isValid = null; |
|
|
|
|
for (const _filter of filters) { |
|
|
|
|
const filter = _filter instanceof Filter ? _filter : new Filter(_filter); |
|
|
|
|
let res; |
|
|
|
|
const column = await filter.getColumn(); |
|
|
|
|
const field = column.title; |
|
|
|
|
let val = data[field]; |
|
|
|
|
|
|
|
|
|
if ( |
|
|
|
|
[ |
|
|
|
|
UITypes.Date, |
|
|
|
|
UITypes.DateTime, |
|
|
|
|
UITypes.CreatedTime, |
|
|
|
|
UITypes.LastModifiedTime, |
|
|
|
|
].includes(column.uidt) |
|
|
|
|
) { |
|
|
|
|
const dateFormat = |
|
|
|
|
client === 'mysql2' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ'; |
|
|
|
|
|
|
|
|
|
let now = dayjs(new Date()); |
|
|
|
|
const dateFormatFromMeta = column?.meta?.date_format; |
|
|
|
|
const dataVal: any = val; |
|
|
|
|
let filterVal: any = filter.value; |
|
|
|
|
if (dateFormatFromMeta && isDateMonthFormat(dateFormatFromMeta)) { |
|
|
|
|
// reset to 1st
|
|
|
|
|
now = dayjs(now).date(1); |
|
|
|
|
if (val) val = dayjs(val).date(1); |
|
|
|
|
} |
|
|
|
|
if (filterVal) res = dayjs(filterVal).isSame(dataVal, 'day'); |
|
|
|
|
|
|
|
|
|
// handle sub operation
|
|
|
|
|
switch (filter.comparison_sub_op) { |
|
|
|
|
case 'today': |
|
|
|
|
filterVal = now; |
|
|
|
|
break; |
|
|
|
|
case 'tomorrow': |
|
|
|
|
filterVal = now.add(1, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'yesterday': |
|
|
|
|
filterVal = now.add(-1, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'oneWeekAgo': |
|
|
|
|
filterVal = now.add(-1, 'week'); |
|
|
|
|
break; |
|
|
|
|
case 'oneWeekFromNow': |
|
|
|
|
filterVal = now.add(1, 'week'); |
|
|
|
|
break; |
|
|
|
|
case 'oneMonthAgo': |
|
|
|
|
filterVal = now.add(-1, 'month'); |
|
|
|
|
break; |
|
|
|
|
case 'oneMonthFromNow': |
|
|
|
|
filterVal = now.add(1, 'month'); |
|
|
|
|
break; |
|
|
|
|
case 'daysAgo': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
filterVal = now.add(-filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'daysFromNow': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
filterVal = now.add(filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'exactDate': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
break; |
|
|
|
|
// sub-ops for `isWithin` comparison
|
|
|
|
|
case 'pastWeek': |
|
|
|
|
filterVal = now.add(-1, 'week'); |
|
|
|
|
break; |
|
|
|
|
case 'pastMonth': |
|
|
|
|
filterVal = now.add(-1, 'month'); |
|
|
|
|
break; |
|
|
|
|
case 'pastYear': |
|
|
|
|
filterVal = now.add(-1, 'year'); |
|
|
|
|
break; |
|
|
|
|
case 'nextWeek': |
|
|
|
|
filterVal = now.add(1, 'week'); |
|
|
|
|
break; |
|
|
|
|
case 'nextMonth': |
|
|
|
|
filterVal = now.add(1, 'month'); |
|
|
|
|
break; |
|
|
|
|
case 'nextYear': |
|
|
|
|
filterVal = now.add(1, 'year'); |
|
|
|
|
break; |
|
|
|
|
case 'pastNumberOfDays': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
filterVal = now.add(-filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'nextNumberOfDays': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
filterVal = now.add(filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (dayjs.isDayjs(filterVal)) { |
|
|
|
|
res = filterVal.isSame(dataVal, 'day'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (filter.comparison_op) { |
|
|
|
|
case 'isWithin': { |
|
|
|
|
let now = dayjs(new Date()).format(dateFormat).toString(); |
|
|
|
|
now = column.uidt === UITypes.Date ? now.substring(0, 10) : now; |
|
|
|
|
switch (filter.comparison_sub_op) { |
|
|
|
|
case 'pastWeek': |
|
|
|
|
case 'pastMonth': |
|
|
|
|
case 'pastYear': |
|
|
|
|
case 'pastNumberOfDays': |
|
|
|
|
res = dayjs(dataVal).isBetween(filterVal, now, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'nextWeek': |
|
|
|
|
case 'nextMonth': |
|
|
|
|
case 'nextYear': |
|
|
|
|
case 'nextNumberOfDays': |
|
|
|
|
res = dayjs(dataVal).isBetween(now, filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
switch (typeof filter.value) { |
|
|
|
|
case 'boolean': |
|
|
|
|
val = !!data[field]; |
|
|
|
@ -54,6 +165,7 @@ export async function validateCondition(
|
|
|
|
|
val = +data[field]; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (filter.comparison_op) { |
|
|
|
|
case 'eq': |
|
|
|
|
res = val == filter.value; |
|
|
|
@ -103,14 +215,14 @@ export async function validateCondition(
|
|
|
|
|
res = data[field] !== null; |
|
|
|
|
break; |
|
|
|
|
case 'allof': |
|
|
|
|
res = (filter.value?.split(',').map((item) => item.trim()) ?? []).every( |
|
|
|
|
(item) => (data[field]?.split(',') ?? []).includes(item), |
|
|
|
|
); |
|
|
|
|
res = ( |
|
|
|
|
filter.value?.split(',').map((item) => item.trim()) ?? [] |
|
|
|
|
).every((item) => (data[field]?.split(',') ?? []).includes(item)); |
|
|
|
|
break; |
|
|
|
|
case 'anyof': |
|
|
|
|
res = (filter.value?.split(',').map((item) => item.trim()) ?? []).some( |
|
|
|
|
(item) => (data[field]?.split(',') ?? []).includes(item), |
|
|
|
|
); |
|
|
|
|
res = ( |
|
|
|
|
filter.value?.split(',').map((item) => item.trim()) ?? [] |
|
|
|
|
).some((item) => (data[field]?.split(',') ?? []).includes(item)); |
|
|
|
|
break; |
|
|
|
|
case 'nallof': |
|
|
|
|
res = !( |
|
|
|
@ -118,9 +230,9 @@ export async function validateCondition(
|
|
|
|
|
).every((item) => (data[field]?.split(',') ?? []).includes(item)); |
|
|
|
|
break; |
|
|
|
|
case 'nanyof': |
|
|
|
|
res = !(filter.value?.split(',').map((item) => item.trim()) ?? []).some( |
|
|
|
|
(item) => (data[field]?.split(',') ?? []).includes(item), |
|
|
|
|
); |
|
|
|
|
res = !( |
|
|
|
|
filter.value?.split(',').map((item) => item.trim()) ?? [] |
|
|
|
|
).some((item) => (data[field]?.split(',') ?? []).includes(item)); |
|
|
|
|
break; |
|
|
|
|
case 'lt': |
|
|
|
|
res = +data[field] < +filter.value; |
|
|
|
@ -137,108 +249,21 @@ export async function validateCondition(
|
|
|
|
|
res = +data[field] >= +filter.value; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ([UITypes.Date, UITypes.DateTime].includes(column.uidt)) { |
|
|
|
|
const dateFormat = |
|
|
|
|
client === 'mysql2' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ'; |
|
|
|
|
|
|
|
|
|
let now = dayjs(new Date()); |
|
|
|
|
const dateFormatFromMeta = column?.meta?.date_format; |
|
|
|
|
const dataVal: any = val; |
|
|
|
|
let filterVal: any = filter.value; |
|
|
|
|
if (dateFormatFromMeta && isDateMonthFormat(dateFormatFromMeta)) { |
|
|
|
|
// reset to 1st
|
|
|
|
|
now = dayjs(now).date(1); |
|
|
|
|
if (val) val = dayjs(val).date(1); |
|
|
|
|
} |
|
|
|
|
// handle sub operation
|
|
|
|
|
switch (filter.comparison_sub_op) { |
|
|
|
|
case 'today': |
|
|
|
|
filterVal = now; |
|
|
|
|
break; |
|
|
|
|
case 'tomorrow': |
|
|
|
|
filterVal = now.add(1, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'yesterday': |
|
|
|
|
filterVal = now.add(-1, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'oneWeekAgo': |
|
|
|
|
filterVal = now.add(-1, 'week'); |
|
|
|
|
break; |
|
|
|
|
case 'oneWeekFromNow': |
|
|
|
|
filterVal = now.add(1, 'week'); |
|
|
|
|
break; |
|
|
|
|
case 'oneMonthAgo': |
|
|
|
|
filterVal = now.add(-1, 'month'); |
|
|
|
|
break; |
|
|
|
|
case 'oneMonthFromNow': |
|
|
|
|
filterVal = now.add(1, 'month'); |
|
|
|
|
break; |
|
|
|
|
case 'daysAgo': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
filterVal = now.add(-filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'daysFromNow': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
filterVal = now.add(filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'exactDate': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
break; |
|
|
|
|
// sub-ops for `isWithin` comparison
|
|
|
|
|
case 'pastWeek': |
|
|
|
|
filterVal = now.add(-1, 'week'); |
|
|
|
|
break; |
|
|
|
|
case 'pastMonth': |
|
|
|
|
filterVal = now.add(-1, 'month'); |
|
|
|
|
break; |
|
|
|
|
case 'pastYear': |
|
|
|
|
filterVal = now.add(-1, 'year'); |
|
|
|
|
break; |
|
|
|
|
case 'nextWeek': |
|
|
|
|
filterVal = now.add(1, 'week'); |
|
|
|
|
break; |
|
|
|
|
case 'nextMonth': |
|
|
|
|
filterVal = now.add(1, 'month'); |
|
|
|
|
break; |
|
|
|
|
case 'nextYear': |
|
|
|
|
filterVal = now.add(1, 'year'); |
|
|
|
|
break; |
|
|
|
|
case 'pastNumberOfDays': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
filterVal = now.add(-filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
case 'nextNumberOfDays': |
|
|
|
|
if (!filterVal) return; |
|
|
|
|
filterVal = now.add(filterVal, 'day'); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (dayjs.isDayjs(filterVal)) { |
|
|
|
|
// turn `filterVal` in dayjs object format to string
|
|
|
|
|
filterVal = filterVal.format(dateFormat).toString(); |
|
|
|
|
// keep YYYY-MM-DD only for date
|
|
|
|
|
filterVal = |
|
|
|
|
column.uidt === UITypes.Date ? filterVal.substring(0, 10) : filterVal; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return dataVal === filterVal; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (filter.logical_op) { |
|
|
|
|
case 'or': |
|
|
|
|
isValid = isValid || res; |
|
|
|
|
isValid = isValid || !!res; |
|
|
|
|
break; |
|
|
|
|
case 'not': |
|
|
|
|
isValid = isValid && !res; |
|
|
|
|
break; |
|
|
|
|
case 'and': |
|
|
|
|
default: |
|
|
|
|
isValid = isValid && res; |
|
|
|
|
isValid = (isValid ?? true) && res; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return isValid; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|