mirror of
https://github.com/nocodb/nocodb.git
synced 2026-02-02 02:57:23 +00:00
Merge pull request #7076 from nocodb/fix/filter-timezone-column-update
fix: filter timezone when column timezone updated
This commit is contained in:
@@ -553,3 +553,19 @@ export enum ViewSettingOverrideOptions {
|
||||
GROUP = 'group',
|
||||
ROW_COLORING = 'rowColoring',
|
||||
}
|
||||
|
||||
export enum MetaEventType {
|
||||
COLUMN_ADDED = 'COLUMN_ADDED',
|
||||
COLUMN_UPDATED = 'COLUMN_UPDATED',
|
||||
COLUMN_DELETED = 'COLUMN_DELETED',
|
||||
}
|
||||
|
||||
export enum MetaEntityType {
|
||||
BASE = 'BASE',
|
||||
TABLE = 'TABLE',
|
||||
COLUMN = 'COLUMN',
|
||||
VIEW = 'VIEW',
|
||||
FILTER = 'FILTER',
|
||||
SORT = 'SORT',
|
||||
VIEW_ROW_COLOR = 'VIEW_ROW_COLOR',
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { ColumnType, FilterType } from './Api';
|
||||
import {
|
||||
OrgUserRoles,
|
||||
ProjectRoles,
|
||||
WorkspaceUserRoles,
|
||||
} from './enums';
|
||||
import { OrgUserRoles, ProjectRoles, WorkspaceUserRoles } from './enums';
|
||||
import { PlanTitles } from './payment';
|
||||
|
||||
export const enumColors = {
|
||||
@@ -239,7 +235,6 @@ export enum NcErrorType {
|
||||
ERR_SUBSCRIPTION_CREATE_FAILED = 'ERR_SUBSCRIPTION_CREATE_FAILED',
|
||||
ERR_STRIPE_WEBHOOK_VERIFICATION_FAILED = 'ERR_STRIPE_WEBHOOK_VERIFICATION_FAILED',
|
||||
ERR_API_CLIENT_NOT_FOUND = 'ERR_API_CLIENT_NOT_FOUND',
|
||||
|
||||
}
|
||||
|
||||
export enum ROW_COLORING_MODE {
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface NcContext {
|
||||
socket_id?: string;
|
||||
nc_site_url?: string;
|
||||
timezone?: string;
|
||||
suppressDependencyEvaluation?: boolean;
|
||||
}
|
||||
|
||||
export interface NcRequest extends Partial<Request> {
|
||||
|
||||
@@ -47,6 +47,7 @@ import { getUniqueColumnAliasName } from '~/helpers/getUniqueName';
|
||||
import ProjectMgrv2 from '~/db/sql-mgr/v2/ProjectMgrv2';
|
||||
import { ViewRowColorService } from '~/services/view-row-color.service';
|
||||
import { FiltersService } from '~/services/filters.service';
|
||||
import { MetaDependencyEventHandler } from '~/services/meta-dependency/event-handler.service';
|
||||
|
||||
@Injectable()
|
||||
export class ColumnsService extends ColumnsServiceCE {
|
||||
@@ -57,6 +58,7 @@ export class ColumnsService extends ColumnsServiceCE {
|
||||
protected readonly formulaColumnTypeChanger,
|
||||
protected readonly viewRowColorService: ViewRowColorService,
|
||||
protected readonly filtersService: FiltersService,
|
||||
protected readonly metaDependencyEventHandler: MetaDependencyEventHandler,
|
||||
) {
|
||||
super(
|
||||
metaService,
|
||||
@@ -64,6 +66,7 @@ export class ColumnsService extends ColumnsServiceCE {
|
||||
formulaColumnTypeChanger,
|
||||
formulaColumnTypeChanger,
|
||||
filtersService,
|
||||
metaDependencyEventHandler,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -353,11 +353,13 @@ export default class Filter implements FilterType {
|
||||
id,
|
||||
);
|
||||
|
||||
await NocoCache.update(
|
||||
context,
|
||||
`${CacheScope.FILTER_EXP}:${id}`,
|
||||
updateObj,
|
||||
);
|
||||
ncMeta.knex.attachToTransaction(async () => {
|
||||
await NocoCache.update(
|
||||
context,
|
||||
`${CacheScope.FILTER_EXP}:${id}`,
|
||||
updateObj,
|
||||
);
|
||||
});
|
||||
|
||||
// on update delete any optimised single query cache
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Module } from '@nestjs/common';
|
||||
import { MulterModule } from '@nestjs/platform-express';
|
||||
import multer from 'multer';
|
||||
import { NotFoundHandlerModule } from './not-found-handler.module';
|
||||
import { MetaDependencyEventHandler } from '~/services/meta-dependency/event-handler.service';
|
||||
import { ViewsV3Service } from '~/services/v3/views-v3.service';
|
||||
import { EventEmitterModule } from '~/modules/event-emitter/event-emitter.module';
|
||||
import { JobsModule } from '~/modules/jobs/jobs.module';
|
||||
@@ -161,6 +162,10 @@ import {
|
||||
InternalApiModuleProvider,
|
||||
InternalApiModules,
|
||||
} from '~/controllers/internal/provider';
|
||||
import {
|
||||
MetaDependencyModuleProvider,
|
||||
MetaDependencyServices,
|
||||
} from '~/services/meta-dependency/meta-dependency.provider';
|
||||
|
||||
export const nocoModuleMetadata = {
|
||||
imports: [
|
||||
@@ -358,6 +363,11 @@ export const nocoModuleMetadata = {
|
||||
|
||||
...InternalApiModules,
|
||||
InternalApiModuleProvider,
|
||||
|
||||
/* Dependency handler */
|
||||
MetaDependencyEventHandler,
|
||||
...MetaDependencyServices,
|
||||
MetaDependencyModuleProvider,
|
||||
],
|
||||
exports: [
|
||||
/* Generic */
|
||||
@@ -407,6 +417,8 @@ export const nocoModuleMetadata = {
|
||||
AttachmentUrlUploadHandler,
|
||||
|
||||
...InternalApiModules,
|
||||
MetaDependencyEventHandler,
|
||||
...MetaDependencyServices,
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
isSystemColumn,
|
||||
isVirtualCol,
|
||||
LongTextAiMetaProp,
|
||||
MetaEventType,
|
||||
NcApiVersion,
|
||||
NcBaseError,
|
||||
ncIsNull,
|
||||
@@ -103,6 +104,7 @@ import { MetaTable } from '~/utils/globals';
|
||||
import { parseMetaProp } from '~/utils/modelUtils';
|
||||
import NocoSocket from '~/socket/NocoSocket';
|
||||
import { DBErrorExtractor } from '~/helpers/db-error/extractor';
|
||||
import { MetaDependencyEventHandler } from '~/services/meta-dependency/event-handler.service';
|
||||
|
||||
export type { ReusableParams } from '~/services/columns.service.type';
|
||||
|
||||
@@ -253,6 +255,7 @@ export class ColumnsService implements IColumnsService {
|
||||
protected readonly formulaColumnTypeChanger: IFormulaColumnTypeChanger,
|
||||
protected readonly viewRowColorService: ViewRowColorService,
|
||||
protected readonly filtersService: FiltersService,
|
||||
protected readonly metaDependencyEventHandler: MetaDependencyEventHandler,
|
||||
) {}
|
||||
|
||||
async updateFormulas(
|
||||
@@ -2056,6 +2059,15 @@ export class ColumnsService implements IColumnsService {
|
||||
context,
|
||||
columns: table.columns,
|
||||
});
|
||||
await this.metaDependencyEventHandler.handleEvent(
|
||||
context,
|
||||
{
|
||||
eventType: MetaEventType.COLUMN_UPDATED,
|
||||
oldEntity: oldColumn,
|
||||
newEntity: updatedColumn,
|
||||
},
|
||||
ncMeta,
|
||||
);
|
||||
|
||||
NocoSocket.broadcastEvent(
|
||||
context,
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
META_DEPENDENCY_MODULE_PROVIDER_KEY,
|
||||
type MetaDependencyEventRequest,
|
||||
type MetaEventHandler,
|
||||
} from './types';
|
||||
import type { MetaEventType, NcContext } from 'nocodb-sdk';
|
||||
import type { MetaService } from '~/meta/meta.service';
|
||||
import Noco from '~/Noco';
|
||||
|
||||
@Injectable()
|
||||
export class MetaDependencyEventHandler {
|
||||
constructor(
|
||||
@Inject(META_DEPENDENCY_MODULE_PROVIDER_KEY)
|
||||
protected readonly metaEventHandlers: MetaEventHandler[],
|
||||
) {
|
||||
this.registerEvents(metaEventHandlers);
|
||||
}
|
||||
|
||||
metaEventHandlerMap: Record<MetaEventType, MetaEventHandler[]> = {
|
||||
COLUMN_ADDED: [],
|
||||
COLUMN_DELETED: [],
|
||||
COLUMN_UPDATED: [],
|
||||
};
|
||||
|
||||
registerEvents(metaEventHandler: MetaEventHandler[]) {
|
||||
for (const each of metaEventHandler) {
|
||||
for (const eachType of each.triggerMetaEvents) {
|
||||
this.metaEventHandlerMap[eachType] =
|
||||
this.metaEventHandlerMap[eachType] ?? [];
|
||||
this.metaEventHandlerMap[eachType].push(each);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleEvent(
|
||||
context: NcContext,
|
||||
param: MetaDependencyEventRequest,
|
||||
ncMeta = Noco.ncMeta,
|
||||
) {
|
||||
// if suppressed, do not make further evaluation
|
||||
if (context.suppressDependencyEvaluation) {
|
||||
return;
|
||||
}
|
||||
// next context will have suppressDependencyEvaluation as true by default unless modules override it.
|
||||
const nextContext = {
|
||||
...context,
|
||||
suppressDependencyEvaluation: true,
|
||||
} as NcContext;
|
||||
let trxNcMeta: MetaService;
|
||||
try {
|
||||
for (const handler of this.metaEventHandlerMap[param.eventType] ?? []) {
|
||||
const affectedDependencies = await handler.getAffectedDependency(
|
||||
nextContext,
|
||||
param,
|
||||
trxNcMeta ?? ncMeta,
|
||||
);
|
||||
if (affectedDependencies) {
|
||||
trxNcMeta = trxNcMeta ?? (await ncMeta.startTransaction());
|
||||
await handler.handle(
|
||||
nextContext,
|
||||
{
|
||||
...param,
|
||||
affectedDependencyResult: affectedDependencies,
|
||||
},
|
||||
trxNcMeta,
|
||||
);
|
||||
}
|
||||
}
|
||||
await trxNcMeta?.commit();
|
||||
} catch (ex) {
|
||||
await trxNcMeta?.rollback();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { MetaEventType, parseProp, UITypes } from 'nocodb-sdk';
|
||||
import type { NcContext } from 'nocodb-sdk';
|
||||
import type {
|
||||
AffectedDependencyResult,
|
||||
MetaDependencyEventRequest,
|
||||
MetaEventHandler,
|
||||
} from '../../types';
|
||||
import { Filter } from '~/models';
|
||||
import { MetaTable } from '~/cli';
|
||||
import { parseMetaProp, stringifyMetaProp } from '~/utils/modelUtils';
|
||||
import Noco from '~/Noco';
|
||||
|
||||
/**
|
||||
* @class ColumnTimezoneUpdateDependencyHandler
|
||||
* @description Handles updates to column timezones and propagates these changes to dependent filters.
|
||||
* This class is responsible for identifying when a column's timezone changes (for DateTime, Date, or Formula types)
|
||||
* and then updating any associated filter expressions that rely on that column's timezone.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ColumnTimezoneUpdateDependencyHandler implements MetaEventHandler {
|
||||
triggerMetaEvents: MetaEventType[] = [MetaEventType.COLUMN_UPDATED];
|
||||
async getAffectedDependency(
|
||||
context: NcContext,
|
||||
param: MetaDependencyEventRequest,
|
||||
ncMeta = Noco.ncMeta,
|
||||
): Promise<AffectedDependencyResult> {
|
||||
let validForProcess = false;
|
||||
const affectedColumnIds: string[] = [];
|
||||
const newEntityMeta = parseProp(param.newEntity.meta);
|
||||
const oldEntityMeta = parseProp(param.oldEntity.meta);
|
||||
if (
|
||||
[UITypes.DateTime, UITypes.Date].includes(param.newEntity.uidt) &&
|
||||
// we leave it as is if the new meta timezone empty / not set
|
||||
newEntityMeta.timezone &&
|
||||
newEntityMeta.timezone !== oldEntityMeta.timezone
|
||||
) {
|
||||
validForProcess = true;
|
||||
affectedColumnIds.push(param.newEntity.id);
|
||||
} else if (
|
||||
[UITypes.Formula].includes(param.newEntity.uidt) &&
|
||||
newEntityMeta.display_column_meta?.timezone &&
|
||||
newEntityMeta.display_column_meta?.timezone !==
|
||||
oldEntityMeta.display_column_meta?.timezone
|
||||
) {
|
||||
validForProcess = true;
|
||||
affectedColumnIds.push(param.newEntity.id);
|
||||
}
|
||||
|
||||
if (validForProcess) {
|
||||
return {
|
||||
filters: await ncMeta.metaList2(
|
||||
context.workspace_id,
|
||||
context.base_id,
|
||||
MetaTable.FILTER_EXP,
|
||||
{
|
||||
xcCondition: (qb) => qb.whereIn('fk_column_id', affectedColumnIds),
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async handle(
|
||||
context: NcContext,
|
||||
param: MetaDependencyEventRequest & {
|
||||
affectedDependencyResult: AffectedDependencyResult;
|
||||
},
|
||||
ncMeta = Noco.ncMeta,
|
||||
): Promise<void> {
|
||||
if (!param.affectedDependencyResult.filters?.length) {
|
||||
return;
|
||||
}
|
||||
let timezone: string;
|
||||
if (
|
||||
[UITypes.DateTime, UITypes.Date].includes(param.newEntity.uidt) &&
|
||||
parseProp(param.newEntity.meta).timezone
|
||||
) {
|
||||
timezone = parseProp(param.newEntity.meta).timezone;
|
||||
} else if (
|
||||
[UITypes.Formula].includes(param.newEntity.uidt) &&
|
||||
parseProp(param.newEntity.meta).display_column_meta?.timezone
|
||||
) {
|
||||
timezone = parseProp(param.newEntity.meta).display_column_meta?.timezone;
|
||||
}
|
||||
for (const each of param.affectedDependencyResult.filters as Filter[]) {
|
||||
each.meta = parseMetaProp(each);
|
||||
each.meta.timezone = timezone;
|
||||
await Filter.update(
|
||||
{
|
||||
...context,
|
||||
base_id: each.base_id,
|
||||
workspace_id: each.fk_workspace_id,
|
||||
},
|
||||
each.id,
|
||||
{ ...each, meta: stringifyMetaProp(each) },
|
||||
ncMeta,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import {
|
||||
META_DEPENDENCY_MODULE_PROVIDER_KEY,
|
||||
type MetaEventHandler,
|
||||
} from './types';
|
||||
import { ColumnTimezoneUpdateDependencyHandler } from '~/services/meta-dependency/handler/column/column-timezone-update.handler';
|
||||
|
||||
export const MetaDependencyServices = [ColumnTimezoneUpdateDependencyHandler];
|
||||
|
||||
export const MetaDependencyModuleProvider = {
|
||||
provide: META_DEPENDENCY_MODULE_PROVIDER_KEY,
|
||||
useFactory: (...internalApiModules: MetaEventHandler[]) => internalApiModules,
|
||||
inject: MetaDependencyServices,
|
||||
};
|
||||
41
packages/nocodb/src/services/meta-dependency/types.ts
Normal file
41
packages/nocodb/src/services/meta-dependency/types.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { MetaEntityType, MetaEventType, NcContext } from 'nocodb-sdk';
|
||||
import type { MetaService } from '~/meta/meta.service';
|
||||
|
||||
export const META_DEPENDENCY_MODULE_PROVIDER_KEY = 'META_DEPENDENCY';
|
||||
|
||||
export interface AffectedDependencyResult {
|
||||
bases?: any[];
|
||||
models?: any[];
|
||||
filters?: any[];
|
||||
columns?: any[];
|
||||
views?: any[];
|
||||
sorts?: any[];
|
||||
}
|
||||
|
||||
export interface MetaDependencyEventRequest {
|
||||
eventType: MetaEventType;
|
||||
oldEntity?: any;
|
||||
newEntity?: any;
|
||||
}
|
||||
|
||||
export interface MetaEventHandler {
|
||||
triggerMetaEvents: MetaEventType[];
|
||||
getAffectedDependency(
|
||||
context: NcContext,
|
||||
param: MetaDependencyEventRequest,
|
||||
ncMeta?: MetaService,
|
||||
): Promise<undefined | AffectedDependencyResult>;
|
||||
handle(
|
||||
context: NcContext,
|
||||
param: MetaDependencyEventRequest & {
|
||||
affectedDependencyResult: AffectedDependencyResult;
|
||||
},
|
||||
ncMeta?: MetaService,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MetaEvent<T> {
|
||||
eventType: MetaEventType;
|
||||
entityType: MetaEntityType;
|
||||
entity: T;
|
||||
}
|
||||
Reference in New Issue
Block a user