mirror of
https://github.com/teableio/teable.git
synced 2026-02-05 04:50:41 +08:00
feat: v2 core logger
This commit is contained in:
parent
409c82f6da
commit
e4e5e2dfd0
@ -4,17 +4,23 @@ import { ConfigService } from '@nestjs/config';
|
||||
import { createV2NodePgContainer } from '@teable/v2-container-node';
|
||||
import { v2PostgresDbTokens } from '@teable/v2-db-postgres';
|
||||
import type { DependencyContainer } from '@teable/v2-di';
|
||||
import { PinoLogger } from 'nestjs-pino';
|
||||
import { PinoLoggerAdapter } from './v2-logger.adapter';
|
||||
|
||||
@Injectable()
|
||||
export class V2ContainerService implements OnModuleDestroy {
|
||||
private containerPromise?: Promise<DependencyContainer>;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly pinoLogger: PinoLogger
|
||||
) {}
|
||||
|
||||
async getContainer(): Promise<DependencyContainer> {
|
||||
if (!this.containerPromise) {
|
||||
const connectionString = this.configService.getOrThrow<string>('PRISMA_DATABASE_URL');
|
||||
this.containerPromise = createV2NodePgContainer({ connectionString });
|
||||
const logger = new PinoLoggerAdapter(this.pinoLogger);
|
||||
this.containerPromise = createV2NodePgContainer({ connectionString, logger });
|
||||
}
|
||||
|
||||
return this.containerPromise;
|
||||
|
||||
38
apps/nestjs-backend/src/features/v2/v2-logger.adapter.ts
Normal file
38
apps/nestjs-backend/src/features/v2/v2-logger.adapter.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import type { ILogger, LogContext } from '@teable/v2-core';
|
||||
import type { PinoLogger } from 'nestjs-pino';
|
||||
|
||||
export class PinoLoggerAdapter implements ILogger {
|
||||
constructor(private readonly logger: PinoLogger) {}
|
||||
|
||||
debug(message: string, context?: LogContext): void {
|
||||
if (context) {
|
||||
this.logger.debug(context, message);
|
||||
return;
|
||||
}
|
||||
this.logger.debug(message);
|
||||
}
|
||||
|
||||
info(message: string, context?: LogContext): void {
|
||||
if (context) {
|
||||
this.logger.info(context, message);
|
||||
return;
|
||||
}
|
||||
this.logger.info(message);
|
||||
}
|
||||
|
||||
warn(message: string, context?: LogContext): void {
|
||||
if (context) {
|
||||
this.logger.warn(context, message);
|
||||
return;
|
||||
}
|
||||
this.logger.warn(message);
|
||||
}
|
||||
|
||||
error(message: string, context?: LogContext): void {
|
||||
if (context) {
|
||||
this.logger.error(context, message);
|
||||
return;
|
||||
}
|
||||
this.logger.error(message);
|
||||
}
|
||||
}
|
||||
29
packages/v2/adapter-logger-console/.eslintrc.cjs
Normal file
29
packages/v2/adapter-logger-console/.eslintrc.cjs
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Specific eslint rules for this workspace, learn how to compose
|
||||
* @link https://github.com/teableio/teable/tree/main/packages/eslint-config-bases
|
||||
*/
|
||||
require('@teable/eslint-config-bases/patch/modern-module-resolution');
|
||||
|
||||
const { getDefaultIgnorePatterns } = require('@teable/eslint-config-bases/helpers');
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: 'tsconfig.eslint.json',
|
||||
},
|
||||
ignorePatterns: [...getDefaultIgnorePatterns()],
|
||||
extends: [
|
||||
'@teable/eslint-config-bases/typescript',
|
||||
'@teable/eslint-config-bases/sonar',
|
||||
'@teable/eslint-config-bases/regexp',
|
||||
'@teable/eslint-config-bases/jest',
|
||||
// Apply prettier and disable incompatible rules
|
||||
'@teable/eslint-config-bases/prettier-plugin',
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-imports': 'off',
|
||||
},
|
||||
overrides: [],
|
||||
};
|
||||
12
packages/v2/adapter-logger-console/.gitignore
vendored
Normal file
12
packages/v2/adapter-logger-console/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# build
|
||||
/dist
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
39
packages/v2/adapter-logger-console/package.json
Normal file
39
packages/v2/adapter-logger-console/package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@teable/v2-adapter-logger-console",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsdown --tsconfig tsconfig.build.json",
|
||||
"dev": "tsdown --tsconfig tsconfig.build.json --watch",
|
||||
"clean": "rimraf ./dist ./coverage ./tsconfig.tsbuildinfo ./tsconfig.build.tsbuildinfo ./.eslintcache",
|
||||
"lint": "eslint . --ext .ts,.js,.mjs,.cjs,.mts,.cts --cache --cache-location ../../../.cache/eslint/v2-adapter-logger-console.eslintcache",
|
||||
"typecheck": "tsc --project ./tsconfig.json --noEmit",
|
||||
"test-unit": "vitest run --silent",
|
||||
"test-unit-cover": "pnpm test-unit --coverage",
|
||||
"fix-all-files": "eslint . --ext .ts,.js,.mjs,.cjs,.mts,.cts --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@teable/v2-core": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@teable/eslint-config-bases": "workspace:^",
|
||||
"@teable/v2-tsdown-config": "workspace:*",
|
||||
"@types/node": "22.18.0",
|
||||
"@vitest/coverage-v8": "4.0.16",
|
||||
"eslint": "8.57.0",
|
||||
"prettier": "3.2.5",
|
||||
"rimraf": "5.0.5",
|
||||
"tsdown": "0.18.1",
|
||||
"typescript": "5.4.3",
|
||||
"vite-tsconfig-paths": "4.3.2",
|
||||
"vitest": "4.0.16"
|
||||
}
|
||||
}
|
||||
30
packages/v2/adapter-logger-console/src/ConsoleLogger.ts
Normal file
30
packages/v2/adapter-logger-console/src/ConsoleLogger.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ILogger, LogContext } from '@teable/v2-core';
|
||||
|
||||
type ConsoleLogFn = (...args: unknown[]) => void;
|
||||
|
||||
export class ConsoleLogger implements ILogger {
|
||||
private log(logFn: ConsoleLogFn, message: string, context?: LogContext): void {
|
||||
if (context) {
|
||||
logFn.call(console, message, context);
|
||||
return;
|
||||
}
|
||||
logFn.call(console, message);
|
||||
}
|
||||
|
||||
debug(message: string, context?: LogContext): void {
|
||||
this.log(console.debug, message, context);
|
||||
}
|
||||
|
||||
info(message: string, context?: LogContext): void {
|
||||
this.log(console.info, message, context);
|
||||
}
|
||||
|
||||
warn(message: string, context?: LogContext): void {
|
||||
this.log(console.warn, message, context);
|
||||
}
|
||||
|
||||
error(message: string, context?: LogContext): void {
|
||||
this.log(console.error, message, context);
|
||||
}
|
||||
}
|
||||
1
packages/v2/adapter-logger-console/src/index.ts
Normal file
1
packages/v2/adapter-logger-console/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './ConsoleLogger';
|
||||
10
packages/v2/adapter-logger-console/tsconfig.build.json
Normal file
10
packages/v2/adapter-logger-console/tsconfig.build.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"paths": {}
|
||||
},
|
||||
"exclude": ["dist", "**/__tests__/**", "**/*.spec.ts", "**/*.test.ts"],
|
||||
"include": ["src"]
|
||||
}
|
||||
20
packages/v2/adapter-logger-console/tsconfig.eslint.json
Normal file
20
packages/v2/adapter-logger-console/tsconfig.eslint.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"exclude": ["node_modules", "**/.*/*", "dist"],
|
||||
"include": [
|
||||
".eslintrc.*",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.mts",
|
||||
"**/*.js",
|
||||
"**/*.cjs",
|
||||
"**/*.mjs",
|
||||
"**/*.jsx",
|
||||
"**/*.json"
|
||||
]
|
||||
}
|
||||
27
packages/v2/adapter-logger-console/tsconfig.json
Normal file
27
packages/v2/adapter-logger-console/tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "@teable/v2-adapter-logger-console",
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"target": "esnext",
|
||||
"lib": ["esnext", "dom"],
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noEmit": false,
|
||||
"incremental": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"declarationDir": "dist",
|
||||
"composite": true,
|
||||
"rootDir": "../",
|
||||
"outDir": "dist",
|
||||
"paths": {
|
||||
"@teable/v2-core": ["../core/src"]
|
||||
},
|
||||
"types": ["vitest/globals", "node"]
|
||||
},
|
||||
"exclude": ["**/node_modules", "**/.*/", "./dist", "./coverage"],
|
||||
"include": ["src", "../core/src"]
|
||||
}
|
||||
5
packages/v2/adapter-logger-console/tsdown.config.ts
Normal file
5
packages/v2/adapter-logger-console/tsdown.config.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { defineConfig } from 'tsdown';
|
||||
|
||||
import { v2TsdownBaseConfig } from '@teable/v2-tsdown-config';
|
||||
|
||||
export default defineConfig(v2TsdownBaseConfig);
|
||||
29
packages/v2/adapter-logger-console/vitest.config.ts
Normal file
29
packages/v2/adapter-logger-console/vitest.config.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { defineConfig, configDefaults } from 'vitest/config';
|
||||
|
||||
const testFiles = ['./src/**/*.{test,spec}.{js,ts}'];
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tsconfigPaths()],
|
||||
cacheDir: '../../../.cache/vitest/v2-adapter-logger-console',
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
passWithNoTests: true,
|
||||
typecheck: {
|
||||
enabled: false,
|
||||
},
|
||||
pool: 'forks',
|
||||
fileParallelism: false,
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
extension: ['.js', '.ts'],
|
||||
include: ['src/**/*'],
|
||||
},
|
||||
clearMocks: true,
|
||||
mockReset: true,
|
||||
restoreMocks: true,
|
||||
include: testFiles,
|
||||
exclude: [...configDefaults.exclude, '**/.next/**'],
|
||||
},
|
||||
});
|
||||
@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { v2PostgresDbConfigSchema } from '@teable/v2-db-postgres';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const v2PostgresStateAdapterConfigSchema = v2PostgresDbConfigSchema.extend({
|
||||
ensureSchema: z.boolean().optional(),
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import {
|
||||
NoopEventPublisher,
|
||||
NoopLogger,
|
||||
NoopTableRepository,
|
||||
NoopTableSchemaRepository,
|
||||
NoopUnitOfWork,
|
||||
@ -23,6 +24,9 @@ export const registerV2BrowserNoopDependencies = (
|
||||
c.register(v2CoreTokens.unitOfWork, NoopUnitOfWork, {
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
c.register(v2CoreTokens.logger, NoopLogger, {
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
return c;
|
||||
};
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"fix-all-files": "eslint . --ext .ts,.js,.mjs,.cjs,.mts,.cts --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@teable/v2-adapter-logger-console": "workspace:*",
|
||||
"@teable/v2-adapter-postgres-ddl": "workspace:*",
|
||||
"@teable/v2-adapter-postgres-state": "workspace:*",
|
||||
"@teable/v2-core": "workspace:*",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { ConsoleLogger } from '@teable/v2-adapter-logger-console';
|
||||
import { registerV2PostgresDdlAdapter } from '@teable/v2-adapter-postgres-ddl';
|
||||
import { registerV2PostgresStateAdapter } from '@teable/v2-adapter-postgres-state';
|
||||
import type { ITableRepository } from '@teable/v2-core';
|
||||
@ -37,6 +38,7 @@ export const createV2NodeTestContainer = async (): Promise<IV2NodeTestContainer>
|
||||
c.register(v2CoreTokens.unitOfWork, PostgresUnitOfWork, {
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
c.registerInstance(v2CoreTokens.logger, new ConsoleLogger());
|
||||
|
||||
const tableRepository = c.resolve<ITableRepository>(v2CoreTokens.tableRepository);
|
||||
const eventPublisher = new MemoryEventPublisher();
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"rootDir": "../",
|
||||
"outDir": "dist",
|
||||
"paths": {
|
||||
"@teable/v2-adapter-logger-console": ["../adapter-logger-console/src"],
|
||||
"@teable/v2-adapter-postgres-ddl": ["../adapter-postgres-ddl/src"],
|
||||
"@teable/v2-adapter-postgres-state": ["../adapter-postgres-state/src"],
|
||||
"@teable/v2-core": ["../core/src"],
|
||||
@ -31,6 +32,7 @@
|
||||
"exclude": ["**/node_modules", "**/.*/", "./dist", "./coverage"],
|
||||
"include": [
|
||||
"src",
|
||||
"../adapter-logger-console/src",
|
||||
"../adapter-postgres-ddl/src",
|
||||
"../adapter-postgres-state/src",
|
||||
"../core/src",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { registerV2PostgresDdlAdapter } from '@teable/v2-adapter-postgres-ddl';
|
||||
import type { IV2PostgresStateAdapterConfig } from '@teable/v2-adapter-postgres-state';
|
||||
import { registerV2PostgresStateAdapter } from '@teable/v2-adapter-postgres-state';
|
||||
import { NoopEventPublisher, v2CoreTokens } from '@teable/v2-core';
|
||||
import { NoopEventPublisher, NoopLogger, v2CoreTokens, type ILogger } from '@teable/v2-core';
|
||||
import { PostgresUnitOfWork } from '@teable/v2-db-postgres';
|
||||
import type { DependencyContainer } from '@teable/v2-di';
|
||||
import { Lifecycle, container } from '@teable/v2-di';
|
||||
@ -10,6 +10,7 @@ export interface IV2NodePgContainerOptions {
|
||||
connectionString?: string;
|
||||
ensureSchema?: boolean;
|
||||
seed?: Partial<IV2PostgresStateAdapterConfig['seed']>;
|
||||
logger?: ILogger;
|
||||
}
|
||||
|
||||
export const registerV2NodePgDependencies = async (
|
||||
@ -42,6 +43,14 @@ export const registerV2NodePgDependencies = async (
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
|
||||
if (options.logger) {
|
||||
c.registerInstance(v2CoreTokens.logger, options.logger);
|
||||
} else {
|
||||
c.register(v2CoreTokens.logger, NoopLogger, {
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
}
|
||||
|
||||
return c;
|
||||
};
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ module.exports = {
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
'no-console': 'error',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
|
||||
@ -5,12 +5,13 @@ import type { Result } from 'neverthrow';
|
||||
import type { IDomainEvent } from '../domain/shared/DomainEvent';
|
||||
import type { Table } from '../domain/table/Table';
|
||||
import { Table as TableAggregate } from '../domain/table/Table';
|
||||
import { IEventPublisher } from '../ports/EventPublisher';
|
||||
import type { IExecutionContext } from '../ports/ExecutionContext';
|
||||
import type { IEventPublisher } from '../ports/EventPublisher';
|
||||
import type { ITableRepository } from '../ports/TableRepository';
|
||||
import type { ITableSchemaRepository } from '../ports/TableSchemaRepository';
|
||||
import { ILogger } from '../ports/Logger';
|
||||
import { ITableRepository } from '../ports/TableRepository';
|
||||
import { ITableSchemaRepository } from '../ports/TableSchemaRepository';
|
||||
import { v2CoreTokens } from '../ports/tokens';
|
||||
import type { IUnitOfWork } from '../ports/UnitOfWork';
|
||||
import { IUnitOfWork } from '../ports/UnitOfWork';
|
||||
import type { CreateTableCommand } from './CreateTableCommand';
|
||||
|
||||
export class CreateTableResult {
|
||||
@ -33,6 +34,8 @@ export class CreateTableHandler {
|
||||
private readonly tableSchemaRepository: ITableSchemaRepository,
|
||||
@inject(v2CoreTokens.eventPublisher)
|
||||
private readonly eventPublisher: IEventPublisher,
|
||||
@inject(v2CoreTokens.logger)
|
||||
private readonly logger: ILogger,
|
||||
@inject(v2CoreTokens.unitOfWork)
|
||||
private readonly unitOfWork: IUnitOfWork
|
||||
) {}
|
||||
@ -41,6 +44,14 @@ export class CreateTableHandler {
|
||||
context: IExecutionContext,
|
||||
command: CreateTableCommand
|
||||
): Promise<Result<CreateTableResult, string>> {
|
||||
this.logger.info('CreateTableHandler.start', {
|
||||
actorId: context.actorId.toString(),
|
||||
baseId: command.baseId.toString(),
|
||||
tableName: command.tableName.toString(),
|
||||
fieldCount: command.fields.length,
|
||||
viewCount: command.views.length,
|
||||
});
|
||||
|
||||
const tableResult = this.buildTable(command);
|
||||
if (tableResult.isErr()) return err(tableResult.error);
|
||||
const table = tableResult.value;
|
||||
@ -64,6 +75,12 @@ export class CreateTableHandler {
|
||||
const publishResult = this.eventPublisher.publishMany(context, events);
|
||||
if (publishResult.isErr()) return err(publishResult.error);
|
||||
|
||||
this.logger.info('CreateTableHandler.success', {
|
||||
baseId: command.baseId.toString(),
|
||||
tableId: table.id().toString(),
|
||||
eventCount: events.length,
|
||||
});
|
||||
|
||||
return ok(CreateTableResult.create(table, events));
|
||||
}
|
||||
|
||||
|
||||
@ -76,6 +76,7 @@ export type { PluginView } from './domain/table/views/types/PluginView';
|
||||
|
||||
export * from './ports/EventPublisher';
|
||||
export * from './ports/ExecutionContext';
|
||||
export * from './ports/Logger';
|
||||
export * from './ports/TableRepository';
|
||||
export * from './ports/TableSchemaRepository';
|
||||
export * from './ports/UnitOfWork';
|
||||
|
||||
8
packages/v2/core/src/ports/Logger.ts
Normal file
8
packages/v2/core/src/ports/Logger.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export type LogContext = Readonly<Record<string, unknown>>;
|
||||
|
||||
export interface ILogger {
|
||||
debug(message: string, context?: LogContext): void;
|
||||
info(message: string, context?: LogContext): void;
|
||||
warn(message: string, context?: LogContext): void;
|
||||
error(message: string, context?: LogContext): void;
|
||||
}
|
||||
12
packages/v2/core/src/ports/defaults/NoopLogger.ts
Normal file
12
packages/v2/core/src/ports/defaults/NoopLogger.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import type { ILogger, LogContext } from '../Logger';
|
||||
|
||||
export class NoopLogger implements ILogger {
|
||||
debug(_: string, __?: LogContext): void {}
|
||||
|
||||
info(_: string, __?: LogContext): void {}
|
||||
|
||||
warn(_: string, __?: LogContext): void {}
|
||||
|
||||
error(_: string, __?: LogContext): void {}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
export * from './NoopEventPublisher';
|
||||
export * from './NoopLogger';
|
||||
export * from './NoopTableRepository';
|
||||
export * from './NoopTableSchemaRepository';
|
||||
export * from './NoopUnitOfWork';
|
||||
|
||||
@ -3,4 +3,5 @@ export const v2CoreTokens = {
|
||||
tableSchemaRepository: Symbol('v2.core.tableSchemaRepository'),
|
||||
eventPublisher: Symbol('v2.core.eventPublisher'),
|
||||
unitOfWork: Symbol('v2.core.unitOfWork'),
|
||||
logger: Symbol('v2.core.logger'),
|
||||
} as const;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import {
|
||||
ActorId,
|
||||
CreateTableCommand,
|
||||
@ -9,7 +11,7 @@ import {
|
||||
v2CoreTokens,
|
||||
} from '@teable/v2-core';
|
||||
import { err } from 'neverthrow';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { getV2NodeTestContainer } from '../testkit/v2NodeTestContainer';
|
||||
|
||||
@ -18,57 +20,81 @@ describe('CreateTableHandler', () => {
|
||||
const { container, tableRepository, eventPublisher, baseId } = getV2NodeTestContainer();
|
||||
const handler = container.resolve(CreateTableHandler);
|
||||
|
||||
const commandResult = CreateTableCommand.create({
|
||||
baseId: baseId.toString(),
|
||||
name: 'Projects',
|
||||
fields: [
|
||||
{ type: 'singleLineText', name: 'Name', options: { defaultValue: 'Project' } },
|
||||
{
|
||||
type: 'rating',
|
||||
name: 'Priority',
|
||||
options: { max: 5, icon: 'star', color: 'yellowBright' },
|
||||
},
|
||||
{
|
||||
type: 'singleSelect',
|
||||
name: 'Status',
|
||||
options: {
|
||||
choices: [
|
||||
{ name: 'Todo', color: 'blue' },
|
||||
{ name: 'Doing', color: 'yellow' },
|
||||
{ name: 'Done', color: 'green' },
|
||||
],
|
||||
const infoSpy = vi.spyOn(console, 'info').mockImplementation(() => {});
|
||||
try {
|
||||
const commandResult = CreateTableCommand.create({
|
||||
baseId: baseId.toString(),
|
||||
name: 'Projects',
|
||||
fields: [
|
||||
{ type: 'singleLineText', name: 'Name', options: { defaultValue: 'Project' } },
|
||||
{
|
||||
type: 'rating',
|
||||
name: 'Priority',
|
||||
options: { max: 5, icon: 'star', color: 'yellowBright' },
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
{
|
||||
type: 'singleSelect',
|
||||
name: 'Status',
|
||||
options: {
|
||||
choices: [
|
||||
{ name: 'Todo', color: 'blue' },
|
||||
{ name: 'Doing', color: 'yellow' },
|
||||
{ name: 'Done', color: 'green' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(commandResult.isOk()).toBe(true);
|
||||
if (commandResult.isErr()) return;
|
||||
expect(commandResult.isOk()).toBe(true);
|
||||
if (commandResult.isErr()) return;
|
||||
|
||||
const actorIdResult = ActorId.create('system');
|
||||
expect(actorIdResult.isOk()).toBe(true);
|
||||
if (actorIdResult.isErr()) return;
|
||||
const actorIdResult = ActorId.create('system');
|
||||
expect(actorIdResult.isOk()).toBe(true);
|
||||
if (actorIdResult.isErr()) return;
|
||||
|
||||
const context = { actorId: actorIdResult.value };
|
||||
const result = await handler.handle(context, commandResult.value);
|
||||
expect(result.isOk()).toBe(true);
|
||||
if (result.isErr()) return;
|
||||
const context = { actorId: actorIdResult.value };
|
||||
const result = await handler.handle(context, commandResult.value);
|
||||
expect(result.isOk()).toBe(true);
|
||||
if (result.isErr()) return;
|
||||
|
||||
expect(eventPublisher.events().some((e) => e instanceof TableCreated)).toBe(true);
|
||||
expect(result.value.table.primaryFieldId().equals(result.value.table.fields()[0].id())).toBe(
|
||||
true
|
||||
);
|
||||
expect(result.value.table.baseId().equals(baseId)).toBe(true);
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
'CreateTableHandler.start',
|
||||
expect.objectContaining({
|
||||
actorId: 'system',
|
||||
baseId: baseId.toString(),
|
||||
tableName: 'Projects',
|
||||
fieldCount: 3,
|
||||
viewCount: 1,
|
||||
})
|
||||
);
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
'CreateTableHandler.success',
|
||||
expect.objectContaining({
|
||||
baseId: baseId.toString(),
|
||||
tableId: result.value.table.id().toString(),
|
||||
eventCount: result.value.events.length,
|
||||
})
|
||||
);
|
||||
|
||||
const specResult = Table.specs(baseId).byId(result.value.table.id()).build();
|
||||
expect(specResult.isOk()).toBe(true);
|
||||
if (specResult.isErr()) return;
|
||||
const savedResult = await tableRepository.findOne(context, specResult.value);
|
||||
expect(savedResult.isOk()).toBe(true);
|
||||
if (savedResult.isOk()) {
|
||||
expect(savedResult.value.primaryFieldId().equals(result.value.table.primaryFieldId())).toBe(
|
||||
expect(eventPublisher.events().some((e) => e instanceof TableCreated)).toBe(true);
|
||||
expect(result.value.table.primaryFieldId().equals(result.value.table.fields()[0].id())).toBe(
|
||||
true
|
||||
);
|
||||
expect(result.value.table.baseId().equals(baseId)).toBe(true);
|
||||
|
||||
const specResult = Table.specs(baseId).byId(result.value.table.id()).build();
|
||||
expect(specResult.isOk()).toBe(true);
|
||||
if (specResult.isErr()) return;
|
||||
const savedResult = await tableRepository.findOne(context, specResult.value);
|
||||
expect(savedResult.isOk()).toBe(true);
|
||||
if (savedResult.isOk()) {
|
||||
expect(savedResult.value.primaryFieldId().equals(result.value.table.primaryFieldId())).toBe(
|
||||
true
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
infoSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user