Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion apps/nestjs-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"pre-test-e2e": "cross-env NODE_ENV=test pnpm -F @teable/db-main-prisma prisma-db-seed -- --e2e",
"test-e2e": "pnpm pre-test-e2e && vitest run --config ./vitest-e2e.config.ts --silent",
"test-e2e-cover": "pnpm test-e2e --coverage --bail 1 ${VITEST_SHARD:+--shard=$VITEST_SHARD}",
"typecheck": "tsc --project ./tsconfig.json --noEmit",
"typecheck": "tsc --project ./tsconfig.typecheck.json --noEmit",
"lint": "eslint . --ext .ts,.js,.cjs,.mjs,.mdx --cache --cache-location ../../.cache/eslint/nestjs-backend.eslintcache",
"fix-all-files": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.mdx --fix",
"flamegraph-home": "npx 0x --output-dir './.debug/flamegraph/{pid}.0x' --on-port 'autocannon http://localhost:$PORT --duration 20' -- node ../../node_modules/.bin/next start",
Expand Down
2 changes: 1 addition & 1 deletion apps/nestjs-backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { AttachmentsModule } from './features/attachments/attachments.module';
import { AuthModule } from './features/auth/auth.module';
import { BaseModule } from './features/base/base.module';
import { BaseNodeModule } from './features/base-node/base-node.module';
import { BaseShareModule } from './features/base-share/base-share.module';
import { BuiltinAssetsInitModule } from './features/builtin-assets-init';
import { CanaryModule } from './features/canary';
import { ChatModule } from './features/chat/chat.module';
Expand All @@ -40,7 +41,6 @@ import { PluginPanelModule } from './features/plugin-panel/plugin-panel.module';
import { SelectionModule } from './features/selection/selection.module';
import { AdminOpenApiModule } from './features/setting/open-api/admin-open-api.module';
import { SettingOpenApiModule } from './features/setting/open-api/setting-open-api.module';
import { BaseShareModule } from './features/base-share/base-share.module';
import { ShareModule } from './features/share/share.module';
import { SpaceModule } from './features/space/space.module';
import { TemplateOpenApiModule } from './features/template/template-open-api.module';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ export class TrashListener {
case Events.APP_DELETE: {
resourceId = payload.appId;
resourceType = ResourceType.App;
const app = await this.prismaService.app.findUnique({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const app = await (this.prismaService as any).app.findUnique({
where: { id: resourceId },
select: { id: true, baseId: true, deletedTime: true },
});
Expand All @@ -86,7 +87,8 @@ export class TrashListener {
case Events.WORKFLOW_DELETE: {
resourceId = payload.workflowId;
resourceType = ResourceType.Workflow;
const workflow = await this.prismaService.workflow.findUnique({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const workflow = await (this.prismaService as any).workflow.findUnique({
where: { id: resourceId },
select: { id: true, baseId: true, deletedTime: true },
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
type CallHandler,
Logger,
} from '@nestjs/common';
import * as Sentry from '@sentry/nestjs';
import { trace } from '@opentelemetry/api';
import * as Sentry from '@sentry/nestjs';
import type { Response } from 'express';
import { ClsService } from 'nestjs-cls';
import type { Observable } from 'rxjs';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import { FormulaFieldDto } from '../model/field-dto/formula-field.dto';
import type { LinkFieldDto } from '../model/field-dto/link-field.dto';
import { RollupFieldDto } from '../model/field-dto/rollup-field.dto';

// eslint-disable-next-line @typescript-eslint/naming-convention
type LinkFieldReference = Pick<IFieldVo, 'name' | 'isMultipleCellValue'> & {
options: Pick<ILinkFieldOptionsRo, 'relationship' | 'foreignTableId'> &
Partial<Pick<ILinkFieldOptions, 'fkHostTableName' | 'selfKeyName' | 'foreignKeyName'>>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class CreatedTimeFieldDto extends CreatedTimeFieldCore implements FieldBa
return input.toISOString();
}
if (typeof input === 'string') {
const hasTimezone = /[zZ]|[+-]\d{2}:\d{2}$/.test(input);
const hasTimezone = /z|[+-]\d{2}:\d{2}$/i.test(input);
const parsed = new Date(hasTimezone ? input : `${input}Z`);
if (!Number.isNaN(parsed.getTime())) {
return parsed.toISOString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class LastModifiedTimeFieldDto extends LastModifiedTimeFieldCore implemen
return input.toISOString();
}
if (typeof input === 'string') {
const hasTimezone = /[zZ]|[+-]\d{2}:\d{2}$/.test(input);
const hasTimezone = /z|[+-]\d{2}:\d{2}$/i.test(input);
const parsed = new Date(hasTimezone ? input : `${input}Z`);
if (!Number.isNaN(parsed.getTime())) {
return parsed.toISOString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { ViewOpenApiModule } from '../../view/open-api/view-open-api.module';
import { ViewModule } from '../../view/view.module';
import { FieldCalculateModule } from '../field-calculate/field-calculate.module';
import { FieldModule } from '../field.module';
import { FieldOpenApiController } from './field-open-api.controller';
import { FieldOpenApiV2Service } from './field-open-api-v2.service';
import { FieldOpenApiController } from './field-open-api.controller';
import { FieldOpenApiService } from './field-open-api.service';

@Module({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { FieldModule } from '../field/field.module';
import { TableDomainQueryModule } from '../table-domain';
import { V2Module } from '../v2/v2.module';
import { ForeignKeyIntegrityService } from './foreign-key.service';
import { IntegrityController } from './integrity.controller';
import { IntegrityV2Controller } from './integrity-v2.controller';
import { IntegrityV2Service } from './integrity-v2.service';
import { IntegrityController } from './integrity.controller';
import { LinkFieldIntegrityService } from './link-field.service';
import { LinkIntegrityService } from './link-integrity.service';
import { UniqueIndexService } from './unique-index.service';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('RecordModifySharedService', () => {
try {
getEffectFieldInstances(
table,
// eslint-disable-next-line @typescript-eslint/naming-convention
[{ Name: 'Task A', 'Source ID 2': 'source-1' }],
FieldKeyType.Name
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { ModuleRef } from '@nestjs/core';
import { OnEvent } from '@nestjs/event-emitter';
import { IUserInfoVo } from '@teable/openapi';
import { EventEmitterService } from '../../event-emitter/event-emitter.service';
import { Events } from '../../event-emitter/events';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ describe('TableOpenApiV2Service.duplicateTable', () => {
vi.clearAllMocks();
});

/* eslint-disable sonarjs/no-identical-functions */
const createService = (overrides?: {
tableService?: Record<string, unknown>;
fieldOpenApiService?: Record<string, unknown>;
Expand Down Expand Up @@ -272,6 +273,10 @@ describe('TableOpenApiV2Service.duplicateTable', () => {
...overrides?.dbProvider,
} as never
);
/* eslint-enable sonarjs/no-identical-functions */

const tblDuplicatedId = 'tblDuplicated';
const duplicatedTableName = 'Orders Copy';

it('rebuilds the legacy duplicate-table response from the duplicated v2 table', async () => {
executeDuplicateTableEndpoint.mockResolvedValue({
Expand All @@ -280,7 +285,7 @@ describe('TableOpenApiV2Service.duplicateTable', () => {
ok: true,
data: {
table: {
id: 'tblDuplicated',
id: tblDuplicatedId,
},
fieldIdMap: {
fldSource: 'fldDuplicated',
Expand All @@ -295,8 +300,8 @@ describe('TableOpenApiV2Service.duplicateTable', () => {

const tableService = {
getTableMeta: vi.fn().mockResolvedValue({
id: 'tblDuplicated',
name: 'Orders Copy',
id: tblDuplicatedId,
name: duplicatedTableName,
dbTableName: 'bseTest.orders_copy',
defaultViewId: 'viwDuplicated',
}),
Expand Down Expand Up @@ -357,7 +362,7 @@ describe('TableOpenApiV2Service.duplicateTable', () => {
});

const result = await service.duplicateTable('bseTest', 'tblSource', {
name: 'Orders Copy',
name: duplicatedTableName,
includeRecords: true,
});

Expand All @@ -366,7 +371,7 @@ describe('TableOpenApiV2Service.duplicateTable', () => {
{
baseId: 'bseTest',
tableId: 'tblSource',
name: 'Orders Copy',
name: duplicatedTableName,
includeRecords: true,
},
{}
Expand Down Expand Up @@ -400,17 +405,17 @@ describe('TableOpenApiV2Service.duplicateTable', () => {
enableShare: true,
},
});
expect(tableService.getTableMeta).toHaveBeenCalledWith('bseTest', 'tblDuplicated');
expect(tableService.getTableMeta).toHaveBeenCalledWith('bseTest', tblDuplicatedId);
expect(fieldOpenApiService.getFields).toHaveBeenNthCalledWith(1, 'tblSource', {
filterHidden: false,
});
expect(fieldOpenApiService.getFields).toHaveBeenNthCalledWith(2, 'tblDuplicated', {
expect(fieldOpenApiService.getFields).toHaveBeenNthCalledWith(2, tblDuplicatedId, {
filterHidden: false,
});
expect(viewService.getViews).toHaveBeenCalledWith('tblDuplicated');
expect(viewService.getViews).toHaveBeenCalledWith(tblDuplicatedId);
expect(result).toMatchObject({
id: 'tblDuplicated',
name: 'Orders Copy',
id: tblDuplicatedId,
name: duplicatedTableName,
fieldMap: {
fldSource: 'fldDuplicated',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable sonarjs/no-duplicate-string */
import { Injectable, Logger } from '@nestjs/common';
import type { IRedoVo, IUndoVo } from '@teable/openapi';
import { RedoCommand, RedoResult, UndoCommand, UndoResult, v2CoreTokens } from '@teable/v2-core';
import type { ICommandBus } from '@teable/v2-core';
import { RedoCommand, UndoCommand, v2CoreTokens } from '@teable/v2-core';
import type { ICommandBus, RedoResult, UndoResult } from '@teable/v2-core';
import { ClsService } from 'nestjs-cls';
import { CacheService } from '../../../cache/cache.service';
import type { ICacheStore } from '../../../cache/types';
Expand All @@ -15,8 +15,10 @@ import { buildUndoRedoEnginePreferenceKey } from './undo-redo-engine-preference'

export const X_TEABLE_UNDO_REDO_ENGINE_HEADER = 'x-teable-undo-redo-engine';

// eslint-disable-next-line @typescript-eslint/naming-convention
export type UndoRedoEngine = 'v1' | 'v2';

// eslint-disable-next-line @typescript-eslint/naming-convention
type UndoRedoResponse<T extends IUndoVo | IRedoVo> = {
body: T;
engine: UndoRedoEngine;
Expand Down
6 changes: 3 additions & 3 deletions apps/nestjs-backend/src/features/v2/v2-container.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import type { OnModuleDestroy } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { KeyvUndoRedoStore } from '@teable/v2-adapter-undo-redo-keyv';
import { v2PostgresDbTokens } from '@teable/v2-adapter-db-postgres-pg';
import {
ShareDbPubSubPublisher,
registerV2ShareDbRealtime,
} from '@teable/v2-adapter-realtime-sharedb';
import { v2CoreTokens } from '@teable/v2-core';
import { KeyvUndoRedoStore } from '@teable/v2-adapter-undo-redo-keyv';
import { createV2NodePgContainer } from '@teable/v2-container-node';
import { v2CoreTokens } from '@teable/v2-core';
import type { DependencyContainer } from '@teable/v2-di';
import { registerV2ImportServices } from '@teable/v2-import';
import { PinoLogger } from 'nestjs-pino';
import { ShareDbService } from '../../share-db/share-db.service';
import { CacheService } from '../../cache/cache.service';
import { IThresholdConfig, ThresholdConfig } from '../../configs/threshold.config';
import { ShareDbService } from '../../share-db/share-db.service';
import { V2ActionTriggerService } from './v2-action-trigger.service';
import { CommandBusTracingMiddleware } from './v2-command-bus-tracing.middleware';
import { PinoLoggerAdapter } from './v2-logger.adapter';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { FieldDeleted, ProjectionHandler, ok } from '@teable/v2-core';
import type { DomainError, IEventHandler, IExecutionContext, Result } from '@teable/v2-core';
import type { DependencyContainer } from '@teable/v2-di';
import { ViewService } from '../view/view.service';
import { V2ContainerService } from './v2-container.service';
import { V2_FIELD_DELETE_COMPAT_CONTEXT_KEY } from './v2-field-delete-compat.constants';
import type { IV2FieldDeleteCompatContext } from './v2-field-delete-compat.constants';
import { V2ContainerService } from './v2-container.service';
import type { IV2ProjectionRegistrar } from './v2-projection-registrar';

const getFieldDeleteCompatContext = (
Expand Down
2 changes: 1 addition & 1 deletion apps/nestjs-backend/src/features/v2/v2.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { ViewModule } from '../view/view.module';
import { V2ActionTriggerService } from './v2-action-trigger.service';
import { V2BaseNodeCompatService } from './v2-base-node-compat.service';
import { V2ContainerService } from './v2-container.service';
import { V2Controller } from './v2.controller';
import { V2ExecutionContextFactory } from './v2-execution-context.factory';
import { V2FieldDeleteCompatService } from './v2-field-delete-compat.service';
import { V2OpenApiController } from './v2-openapi.controller';
import { V2RecordHistoryService } from './v2-record-history.service';
import { V2UserRenamePropagationService } from './v2-user-rename-propagation.service';
import { V2Controller } from './v2.controller';

const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === 'object' && value !== null;
Expand Down
1 change: 1 addition & 0 deletions apps/nestjs-backend/src/types/cls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { IPerformanceCacheStore } from '../performance-cache';
import type { IRawOpMap } from '../share-db/interface';
import type { IDataLoaderCache } from './data-loader';

// eslint-disable-next-line @typescript-eslint/naming-convention
export type V2Reason =
| 'env_force_v2_all'
| 'config_force_v2_all'
Expand Down
9 changes: 9 additions & 0 deletions apps/nestjs-backend/src/types/websocket-json-stream.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module '@teamwork/websocket-json-stream' {
import { Duplex } from 'stream';

class WebSocketJSONStream extends Duplex {
constructor(ws: unknown);
}

export default WebSocketJSONStream;
}
10 changes: 6 additions & 4 deletions apps/nestjs-backend/src/utils/filter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ const createField = (partial: Partial<IFieldInstance>): IFieldInstance =>
}) as IFieldInstance;

describe('generateFilterItem', () => {
const testValue = 'Supplier A';

it('uses isNotExactly for multi-value singleSelect fields', () => {
const field = createField({
type: FieldType.SingleSelect,
cellValueType: CellValueType.String,
isMultipleCellValue: true,
});

const result = generateFilterItem(field, ['Supplier A']);
const result = generateFilterItem(field, [testValue]);

expect(result.operator).toBe(isNotExactly.value);
expect(result.value).toEqual(['Supplier A']);
expect(result.value).toEqual([testValue]);
});

it('keeps isNot for single-value singleSelect fields', () => {
Expand All @@ -32,9 +34,9 @@ describe('generateFilterItem', () => {
isMultipleCellValue: false,
});

const result = generateFilterItem(field, 'Supplier A');
const result = generateFilterItem(field, testValue);

expect(result.operator).toBe(isNot.value);
expect(result.value).toBe('Supplier A');
expect(result.value).toBe(testValue);
});
});
1 change: 1 addition & 0 deletions apps/nestjs-backend/test/base-share.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ describe('BaseShareController (e2e)', () => {
});
expect(authRes.status).toEqual(200);
expect(authRes.data.token).toBeDefined();
// eslint-disable-next-line sonarjs/no-duplicate-string
expect(authRes.headers['set-cookie']).toBeDefined();
});

Expand Down
15 changes: 3 additions & 12 deletions apps/nestjs-backend/test/comment-count-collapsed-group.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import type { INestApplication } from '@nestjs/common';
import type { IFieldVo, IFilter, IGroup } from '@teable/core';
import { Colors, FieldKeyType, FieldType, SortFunc } from '@teable/core';
import {
CommentNodeType,
GroupPointType,
createComment,
getCommentCount,
} from '@teable/openapi';
import { CommentNodeType, GroupPointType, createComment, getCommentCount } from '@teable/openapi';
import type { IGroupHeaderPoint, ITableFullVo } from '@teable/openapi';
import {
createField,
Expand Down Expand Up @@ -58,15 +53,11 @@ describe('OpenAPI Comment count with collapsed groups (e2e)', () => {
records: [{ fields: { LookupKey: 'K-1' } }, { fields: { LookupKey: 'K-2' } }],
});

const sourceKeyField = sourceTable.fields.find(
({ name }) => name === 'LookupKey'
) as IFieldVo;
const sourceKeyField = sourceTable.fields.find(({ name }) => name === 'LookupKey') as IFieldVo;
const sourceCategoryField = sourceTable.fields.find(
({ name }) => name === 'Category'
) as IFieldVo;
const hostKeyField = hostTable.fields.find(
({ name }) => name === 'LookupKey'
) as IFieldVo;
const hostKeyField = hostTable.fields.find(({ name }) => name === 'LookupKey') as IFieldVo;

const matchByKeyFilter: IFilter = {
conjunction: 'and',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('Formula conditional numeric cast safety (regression)', () => {
it.skipIf(isForceV2)(
'creates rows successfully when conditional formulas compare malformed numeric text',
async () => {
const malformedPrice = '39.9339.93';
const displayPriceFieldId = generateFieldId();
const table = (await createTable(baseId, {
name: 'formula_conditional_numeric_cast_regression',
Expand All @@ -52,7 +53,7 @@ describe('Formula conditional numeric cast safety (regression)', () => {
records: [
{
fields: {
DisplayPrice: '39.9339.93',
DisplayPrice: malformedPrice,
},
},
{
Expand All @@ -67,12 +68,12 @@ describe('Formula conditional numeric cast safety (regression)', () => {

const targetRecords = records.filter((record) => {
const displayPrice = record.fields.DisplayPrice;
return displayPrice === '39.9339.93' || displayPrice === '39.93';
return displayPrice === malformedPrice || displayPrice === '39.93';
});

expect(targetRecords).toHaveLength(2);
const malformedNumericRecord = targetRecords.find(
(record) => record.fields.DisplayPrice === '39.9339.93'
(record) => record.fields.DisplayPrice === malformedPrice
);
const validNumericRecord = targetRecords.find(
(record) => record.fields.DisplayPrice === '39.93'
Expand Down
Loading
Loading