Skip to content

Commit

Permalink
fix: mount clazzExtension/clazzExtension/tableSql to BaseDao (#220)
Browse files Browse the repository at this point in the history
<!--
Thank you for your pull request. Please review below requirements.
Bug fixes and new features should include tests and possibly benchmarks.
Contributors guide:
https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md

感谢您贡献代码。请确认下列 checklist 的完成情况。
Bug 修复和新功能必须包含测试,必要时请附上性能测试。
Contributors guide:
https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md
-->

##### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to
[x]. -->

- [ ] `npm test` passes
- [ ] tests and/or benchmarks are included
- [ ] documentation is changed or added
- [ ] commit message follows commit guidelines

##### Affected core subsystem(s)
<!-- Provide affected core subsystem(s). -->


##### Description of change
<!-- Provide a description of the change below this comment. -->

<!--
- any feature?
- close https://github.com/eggjs/egg/ISSUE_URL
-->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced new data access utilities and decorators to enhance DAO
functionality across the application.
- Added new paths for extensions, structure, and SQL files in the code
generation process.
- Enhanced the SQL map loading process with improved type safety and
simplified method implementations.

- **Refactor**
- Updated various components to use new DAO loading mechanisms, removing
outdated dependencies.
- Refactored data access layers to improve performance and
maintainability.
- Changed method signatures and class properties to align with updated
data access strategies.

- **Bug Fixes**
- Fixed issues related to incorrect SQL map paths and table model
constructions.

- **Documentation**
- Updated documentation to reflect new features and changes in data
access patterns.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
killagu committed Apr 28, 2024
1 parent afb2a32 commit ac322cf
Show file tree
Hide file tree
Showing 37 changed files with 249 additions and 121 deletions.
2 changes: 2 additions & 0 deletions core/dal-decorator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ export * from './src/decorator/Index';
export * from './src/decorator/Table';
export * from './src/decorator/Column';
export * from './src/decorator/DataSourceQualifier';
export * from './src/decorator/Dao';

export * from './src/util/ColumnInfoUtil';
export * from './src/util/IndexInfoUtil';
export * from './src/util/TableInfoUtil';
export * from './src/util/DaoInfoUtil';

export * from './src/model/ColumnModel';
export * from './src/model/IndexModel';
Expand Down
17 changes: 17 additions & 0 deletions core/dal-decorator/src/decorator/Dao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Prototype, PrototypeUtil } from '@eggjs/core-decorator';
import { StackUtil } from '@eggjs/tegg-common-util';
import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types';
import type { EggProtoImplClass } from '@eggjs/tegg-types';
import { DaoInfoUtil } from '../util/DaoInfoUtil';

export function Dao() {
return function(constructor: EggProtoImplClass) {
DaoInfoUtil.setIsDao(constructor);
const func = Prototype({
accessLevel: AccessLevel.PUBLIC,
initType: ObjectInitType.SINGLETON,
});
func(constructor);
PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5));
};
}
13 changes: 13 additions & 0 deletions core/dal-decorator/src/util/DaoInfoUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { BaseDaoType, DAL_IS_DAO } from '@eggjs/tegg-types';
import type { EggProtoImplClass } from '@eggjs/tegg-types';
import { MetadataUtil } from '@eggjs/core-decorator';

export class DaoInfoUtil {
static setIsDao(clazz: EggProtoImplClass) {
MetadataUtil.defineMetaData(DAL_IS_DAO, true, clazz);
}

static getIsDao(clazz: EggProtoImplClass): clazz is BaseDaoType {
return MetadataUtil.getOwnMetaData(DAL_IS_DAO, clazz) === true;
}
}
1 change: 1 addition & 0 deletions core/dal-runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './src/DataSource';
export * from './src/MySqlDataSource';
export * from './src/TableModelInstanceBuilder';
export * from './src/DatabaseForker';
export * from './src/DaoLoader';
3 changes: 3 additions & 0 deletions core/dal-runtime/src/CodeGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export class CodeGenerator {
id: tableModel.columns.find(t => t.propertyName === 'id'),
primaryIndex: tableModel.getPrimary(),
tableModelPath: TemplateUtil.importPath(tableModelAbsolutePath, path.dirname(filePath)),
extensionPath: `../../extension/${tableModel.clazz.name}Extension`,
structurePath: `../../structure/${tableModel.clazz.name}.json`,
sqlPath: `../../structure/${tableModel.clazz.name}.sql`,
columnMap: tableModel.columns.reduce<Record<string, ColumnModel>>((p, c) => {
p[c.propertyName] = c;
return p;
Expand Down
14 changes: 14 additions & 0 deletions core/dal-runtime/src/DaoLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { EggLoadUnitType } from '@eggjs/tegg-types';
import { DaoInfoUtil } from '@eggjs/tegg/dal';
import { BaseDaoType } from '@eggjs/tegg-types/dal';
import { LoaderFactory } from '@eggjs/tegg/helper';

export class DaoLoader {
static loadDaos(moduleDir: string): Array<BaseDaoType> {
const loader = LoaderFactory.createLoader(moduleDir, EggLoadUnitType.MODULE);
const clazzList = loader.load();
return clazzList.filter((t): t is BaseDaoType => {
return DaoInfoUtil.getIsDao(t);
});
}
}
25 changes: 8 additions & 17 deletions core/dal-runtime/src/DatabaseForker.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import path from 'node:path';
import fs from 'node:fs/promises';
import assert from 'node:assert';
import { RDSClient } from '@eggjs/rds';
import { DataSourceOptions } from './MySqlDataSource';
import { DaoLoader } from './DaoLoader';

export class DatabaseForker {
private readonly env: string;
Expand All @@ -17,35 +16,27 @@ export class DatabaseForker {
return this.env === 'unittest' && this.options.forkDb;
}

async forkDb(dalDir: string) {
async forkDb(moduleDir: string) {
assert(this.shouldFork(), 'fork db only run in unittest');
// 尽早判断不应该 fork,避免对 rds pool 配置造成污染
try {
await fs.access(dalDir);
} catch (_) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { name, initSql, forkDb, database, ...mysqlOptions } = this.options;
const client = new RDSClient(Object.assign(mysqlOptions));
const conn = await client.getConnection();
await this.doCreateUtDb(conn);
await this.forkTables(conn, dalDir);
await this.forkTables(conn, moduleDir);
conn.release();
await client.end();
}

private async forkTables(conn, dalDir: string) {
const sqlDir = path.join(dalDir, 'structure');
const structureFiles = await fs.readdir(sqlDir);
const sqlFiles = structureFiles.filter(t => t.endsWith('.sql'));
for (const sqlFile of sqlFiles) {
await this.doForkTable(conn, path.join(sqlDir, sqlFile));
private async forkTables(conn, moduleDir: string) {
const daoClazzList = DaoLoader.loadDaos(moduleDir);
for (const clazz of daoClazzList) {
await this.doForkTable(conn, clazz.tableSql);
}
}

private async doForkTable(conn, sqlFileName: string) {
const sqlFile = await fs.readFile(sqlFileName, 'utf8');
private async doForkTable(conn, sqlFile: string) {
const sqls = sqlFile.split(';').filter(t => !!t.trim());
for (const sql of sqls) {
await conn.query(sql);
Expand Down
31 changes: 11 additions & 20 deletions core/dal-runtime/src/SqlMapLoader.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,26 @@
import path from 'node:path';
import { TableModel } from '@eggjs/tegg/dal';
import { BaseDaoType, TableModel } from '@eggjs/tegg/dal';
import type { Logger, SqlMap } from '@eggjs/tegg-types';
import { BaseSqlMapGenerator } from './BaseSqlMap';
import { TableSqlMap } from './TableSqlMap';
import { LoaderUtil } from '@eggjs/tegg/helper';

export class SqlMapLoader {
private readonly logger: Logger;
private readonly tableModel: TableModel;
private readonly sqlMapPath: string;
private readonly clazzExtension: Record<string, SqlMap>;

constructor(tableModel: TableModel, moduleDir: string, logger: Logger) {
this.sqlMapPath = path.join(moduleDir, 'dal/extension', `${tableModel.clazz.name}Extension${LoaderUtil.extension}`);
constructor(tableModel: TableModel, baseDaoClazz: BaseDaoType, logger: Logger) {
this.clazzExtension = baseDaoClazz.clazzExtension;
this.logger = logger;
this.tableModel = tableModel;
}

load(): TableSqlMap {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { default: customSqlMap }: { default: Record<string, SqlMap> } = require(this.sqlMapPath);
const baseSqlMapGenerator = new BaseSqlMapGenerator(this.tableModel, this.logger);
const baseSqlMap = baseSqlMapGenerator.load();
const sqlMap = {
...baseSqlMap,
...customSqlMap,
};
return new TableSqlMap(this.tableModel.clazz.name, sqlMap);
} catch (e) {
e.message = `load sql map ${this.sqlMapPath} failed: ${e.message}`;
throw e;
}
const baseSqlMapGenerator = new BaseSqlMapGenerator(this.tableModel, this.logger);
const baseSqlMap = baseSqlMapGenerator.load();
const sqlMap = {
...baseSqlMap,
...this.clazzExtension,
};
return new TableSqlMap(this.tableModel.clazz.name, sqlMap);
}
}
22 changes: 18 additions & 4 deletions core/dal-runtime/src/templates/base_dao.njk
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,18 @@ Optional<{{clazzName}},
>
{% endmacro %}

import fs from 'node:fs';
import path from 'node:path';
import type { InsertResult, UpdateResult, DeleteResult } from '{{dalPkg}}';
import { SingletonProto, AccessLevel, Inject } from '{{teggPkg}}';
import { Inject } from '{{teggPkg}}';
import { Dao } from '{{teggPkg}}/dal';
import { DataSource, DataSourceInjectName, DataSourceQualifier, ColumnTsType } from '{{dalPkg}}';
import { {{ clazzName }} } from '{{ tableModelPath }}';
import {{ clazzName }}Extension from '{{ extensionPath }}';
import Structure from '{{ structurePath }}';

const SQL = Symbol('Dao#sql');

// empty-line
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
/**
Expand All @@ -80,11 +88,17 @@ type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
* @classdesc 该文件由 {{teggPkg}} 自动生成,请**不要**修改它!
*/
/* istanbul ignore next */
@SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
@Dao()
export class Base{{ clazzName }}DAO {
static clazzModel = {{ clazzName }};
static clazzExtension = {{ clazzName }}Extension;
static tableStature = Structure;
static get tableSql() {
if (!this[SQL]) {
this[SQL] = fs.readFileSync(path.join(__dirname, '../../structure/{{clazzName}}.sql'), 'utf8');
}
return this[SQL];
}

@Inject({
name: DataSourceInjectName,
Expand Down
5 changes: 3 additions & 2 deletions core/dal-runtime/test/DAO.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Foo } from './fixtures/modules/dal/Foo';
import { DataSource } from '../src/DataSource';
import FooDAO from './fixtures/modules/dal/dal/dao/FooDAO';
import { DatabaseForker } from '../src/DatabaseForker';
import { BaseFooDAO } from './fixtures/modules/dal/dal/dao/base/BaseFooDAO';

describe('test/DAO.test.ts', () => {
let dataSource: DataSource<Foo>;
Expand All @@ -24,13 +25,13 @@ describe('test/DAO.test.ts', () => {
forkDb: true,
};
forker = new DatabaseForker('unittest', mysqlOptions);
await forker.forkDb(path.join(__dirname, './fixtures/modules/dal/dal'));
await forker.forkDb(path.join(__dirname, './fixtures/modules/dal'));

const mysql = new MysqlDataSource(mysqlOptions);
await mysql.ready();

tableModel = TableModel.build(Foo);
const sqlMapLoader = new SqlMapLoader(tableModel, path.join(__dirname, './fixtures/modules/dal'), console as any);
const sqlMapLoader = new SqlMapLoader(tableModel, BaseFooDAO, console as any);
const sqlMap = sqlMapLoader.load();
dataSource = new DataSource(tableModel, mysql, sqlMap);
});
Expand Down
5 changes: 3 additions & 2 deletions core/dal-runtime/test/DataSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Foo } from './fixtures/modules/dal/Foo';
import { DataSource } from '../src/DataSource';
import { TableModelInstanceBuilder } from '../src/TableModelInstanceBuilder';
import { DatabaseForker } from '../src/DatabaseForker';
import { BaseFooDAO } from './fixtures/modules/dal/dal/dao/base/BaseFooDAO';

describe('test/Datasource.test.ts', () => {
let dataSource: DataSource<Foo>;
Expand All @@ -25,12 +26,12 @@ describe('test/Datasource.test.ts', () => {
forkDb: true,
};
forker = new DatabaseForker('unittest', mysqlOptions);
await forker.forkDb(path.join(__dirname, './fixtures/modules/dal/dal'));
await forker.forkDb(path.join(__dirname, './fixtures/modules/dal'));
const mysql = new MysqlDataSource(mysqlOptions);
await mysql.ready();

tableModel = TableModel.build(Foo);
const sqlMapLoader = new SqlMapLoader(tableModel, path.join(__dirname, './fixtures/modules/dal'), console as any);
const sqlMapLoader = new SqlMapLoader(tableModel, BaseFooDAO, console as any);
const sqlMap = sqlMapLoader.load();
dataSource = new DataSource(tableModel, mysql, sqlMap);
});
Expand Down
4 changes: 2 additions & 2 deletions core/dal-runtime/test/TableSqlMap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import assert from 'node:assert';
import { TableModel } from '@eggjs/dal-decorator';
import { Foo } from './fixtures/modules/dal/Foo';
import { SqlMapLoader } from '../src/SqlMapLoader';
import path from 'node:path';
import { BaseFooDAO } from './fixtures/modules/dal/dal/dao/base/BaseFooDAO';

describe('test/TableSqlMap.test.ts', () => {
it('custom sql should work', () => {
// const generator = new SqlGenerator();
const fooModel = TableModel.build(Foo);
// const sql = generator.generate(fooModel);
const sqlMapLoader = new SqlMapLoader(fooModel, path.join(__dirname, 'fixtures/modules/dal'), console);
const sqlMapLoader = new SqlMapLoader(fooModel, BaseFooDAO, console);
const tableSqlMap = sqlMapLoader.load();
const sql = tableSqlMap.generate('findAll', {}, 'UTC');
assert.equal(sql, 'SELECT `id`,`name`,`col1`,`bit_column`,`bool_column`,`tiny_int_column`,`small_int_column`,`medium_int_column`,`int_column`,`big_int_column`,`decimal_column`,`float_column`,`double_column`,`date_column`,`date_time_column`,`timestamp_column`,`time_column`,`year_column`,`var_char_column`,`binary_column`,`var_binary_column`,`tiny_blob_column`,`tiny_text_column`,`blob_column`,`text_column`,`medium_blob_column`,`long_blob_column`,`medium_text_column`,`long_text_column`,`enum_column`,`set_column`,`geometry_column`,`point_column`,`line_string_column`,`polygon_column`,`multipoint_column`,`multi_line_string_column`,`multi_polygon_column`,`geometry_collection_column`,`json_column` from egg_foo;');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { InsertResult, UpdateResult, DeleteResult } from '@eggjs/rds/lib/types';
import { SingletonProto, AccessLevel, Inject, ModuleQualifier } from '@eggjs/tegg';
import { DataSource, DataSourceInjectName, DataSourceQualifier } from '@eggjs/tegg/dal';
import { Inject, ModuleQualifier } from '@eggjs/tegg';
import { Dao, DataSource, DataSourceInjectName, DataSourceQualifier } from '@eggjs/tegg/dal';
import { Foo } from '../../../../generate_codes/Foo';
import FooExtension from '../../extension/FooExtension';
import Structure from '../../structure/Foo.json';
import fs from 'node:fs';
import path from 'node:path';
const SQL = Symbol('Dao#sql');

type Optional<T, K extends keyof T> = Omit < T, K > & Partial<T> ;
/**
Expand All @@ -10,13 +15,20 @@ type Optional<T, K extends keyof T> = Omit < T, K > & Partial<T> ;
* @classdesc 该文件由 @eggjs/tegg 自动生成,请**不要**修改它!
*/
/* istanbul ignore next */
@SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
@Dao()
export class BaseFooDAO {
@Inject({
name: DataSourceInjectName,
})
static clazzExtension = FooExtension;
static clazzModel = Foo;
static tableStature = Structure;
static get tableSql() {
if (!this[SQL]) {
this[SQL] = fs.readFileSync(path.join(__dirname, '../../structure/Foo.sql'), 'utf8');
}
return this[SQL];
}
@ModuleQualifier('dal')
@DataSourceQualifier('default.Foo')
protected readonly dataSource: DataSource<Foo> ;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import fs from 'node:fs';
import path from 'node:path';
import type { InsertResult, UpdateResult, DeleteResult } from '@eggjs/dal-decorator';
import { SingletonProto, AccessLevel, Inject } from '@eggjs/tegg';
import { Inject } from '@eggjs/tegg';
import { Dao } from '@eggjs/tegg/dal';
import { DataSource, DataSourceInjectName, DataSourceQualifier, ColumnTsType } from '@eggjs/dal-decorator';
import { Foo } from '../../../Foo';
import FooExtension from '../../extension/FooExtension';
import Structure from '../../structure/Foo.json';
const SQL = Symbol('Dao#sql');

type Optional<T, K extends keyof T> = Omit < T, K > & Partial<T> ;
/**
Expand All @@ -10,11 +16,17 @@ type Optional<T, K extends keyof T> = Omit < T, K > & Partial<T> ;
* @classdesc 该文件由 @eggjs/tegg 自动生成,请**不要**修改它!
*/
/* istanbul ignore next */
@SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
@Dao()
export class BaseFooDAO {
static clazzModel = Foo;
static clazzExtension = FooExtension;
static tableStature = Structure;
static get tableSql() {
if (!this[SQL]) {
this[SQL] = fs.readFileSync(path.join(__dirname, '../../structure/Foo.sql'), 'utf8');
}
return this[SQL];
}
@Inject({
name: DataSourceInjectName,
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import fs from 'node:fs';
import path from 'node:path';
import type { InsertResult, UpdateResult, DeleteResult } from '@eggjs/dal-decorator';
import { SingletonProto, AccessLevel, Inject } from '@eggjs/tegg';
import { Inject } from '@eggjs/tegg';
import { Dao } from '@eggjs/tegg/dal';
import { DataSource, DataSourceInjectName, DataSourceQualifier, ColumnTsType } from '@eggjs/dal-decorator';
import { MultiPrimaryKey } from '../../../MultiPrimaryKey';
import MultiPrimaryKeyExtension from '../../extension/MultiPrimaryKeyExtension';
import Structure from '../../structure/MultiPrimaryKey.json';
const SQL = Symbol('Dao#sql');

type Optional<T, K extends keyof T> = Omit < T, K > & Partial<T> ;
/**
Expand All @@ -10,11 +16,17 @@ type Optional<T, K extends keyof T> = Omit < T, K > & Partial<T> ;
* @classdesc 该文件由 @eggjs/tegg 自动生成,请**不要**修改它!
*/
/* istanbul ignore next */
@SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
@Dao()
export class BaseMultiPrimaryKeyDAO {
static clazzModel = MultiPrimaryKey;
static clazzExtension = MultiPrimaryKeyExtension;
static tableStature = Structure;
static get tableSql() {
if (!this[SQL]) {
this[SQL] = fs.readFileSync(path.join(__dirname, '../../structure/MultiPrimaryKey.sql'), 'utf8');
}
return this[SQL];
}
@Inject({
name: DataSourceInjectName,
})
Expand Down
1 change: 1 addition & 0 deletions core/types/dal/Qualifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const DAL_COLUMN_TYPE_MAP = Symbol('EggPrototype#dalColumnTypeMap');
export const DAL_INDEX_LIST = Symbol('EggPrototype#dalIndexList');
export const DAL_IS_TABLE = Symbol('EggPrototype#dalIsTable');
export const DAL_TABLE_PARAMS = Symbol('EggPrototype#dalTableParams');
export const DAL_IS_DAO = Symbol('EggPrototype#dalIsDao');
Loading

0 comments on commit ac322cf

Please sign in to comment.