Skip to content

Commit

Permalink
feat: the concept of TM is removed from CommonDao
Browse files Browse the repository at this point in the history
You can still achieve bmToTM in the userland code
by calling dao.validateAndConvert().
This is to limit and simplify the API of CommonDao.
  • Loading branch information
kirillgroshkov committed Jan 23, 2024
1 parent 227bca4 commit 8b36327
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 119 deletions.
13 changes: 3 additions & 10 deletions src/commondao/common.dao.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
AnyObject,
BaseDBEntity,
CommonLogger,
ErrorMode,
Expand All @@ -18,7 +17,7 @@ import {
import { CommonDB } from '../common.db'
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model'

export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity, TM> {
export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity> {
/**
* Allows to override the id generation function.
* By default it uses `stringId` from nodejs-lib
Expand Down Expand Up @@ -61,7 +60,6 @@ export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntit

beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>
beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>
beforeBMToTM: (bm: BM) => Partial<TM>

/**
* Allows to access the DBM just after it has been loaded from the DB.
Expand Down Expand Up @@ -131,11 +129,7 @@ export enum CommonDaoLogLevel {
DATA_FULL = 30,
}

export interface CommonDaoCfg<
BM extends BaseDBEntity,
DBM extends BaseDBEntity = BM,
TM extends AnyObject = BM,
> {
export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
db: CommonDB
table: string

Expand All @@ -144,7 +138,6 @@ export interface CommonDaoCfg<
*/
dbmSchema?: ObjectSchema<DBM> | AjvSchema<DBM> | ZodSchema<DBM>
bmSchema?: ObjectSchema<BM> | AjvSchema<BM> | ZodSchema<BM>
tmSchema?: ObjectSchema<TM> | AjvSchema<TM> | ZodSchema<TM>

excludeFromIndexes?: (keyof DBM)[]

Expand Down Expand Up @@ -181,7 +174,7 @@ export interface CommonDaoCfg<
logStarted?: boolean

// Hooks are designed with inspiration from got/ky interface
hooks?: Partial<CommonDaoHooks<BM, DBM, TM>>
hooks?: Partial<CommonDaoHooks<BM, DBM>>

/**
* Defaults to true.
Expand Down
7 changes: 1 addition & 6 deletions src/commondao/common.dao.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ import {
createTestItemsBM,
testItemBMSchema,
testItemDBMSchema,
testItemTMSchema,
TEST_TABLE,
TestItemBM,
TestItemDBM,
TestItemTM,
testItemBMJsonSchema,
testItemDBMJsonSchema,
} from '../testing'
Expand All @@ -36,12 +34,11 @@ import { CommonDaoCfg, CommonDaoLogLevel, CommonDaoSaveBatchOptions } from './co
let throwError = false

const db = new InMemoryDB()
const daoCfg: CommonDaoCfg<TestItemBM, TestItemDBM, TestItemTM> = {
const daoCfg: CommonDaoCfg<TestItemBM, TestItemDBM> = {
table: TEST_TABLE,
db,
dbmSchema: testItemDBMSchema,
bmSchema: testItemBMSchema,
tmSchema: testItemTMSchema,
// logStarted: true,
logLevel: CommonDaoLogLevel.OPERATIONS,
hooks: {
Expand Down Expand Up @@ -74,8 +71,6 @@ test('common', async () => {
expect(await dao.getById('non-existing')).toBeNull()
expect(await dao.getByIdAsDBM(undefined)).toBeNull()
expect(await dao.getByIdAsDBM('123')).toBeNull()
expect(await dao.getByIdAsTM(undefined)).toBeNull()
expect(await dao.getByIdAsTM('123')).toBeNull()

expect(await dao.deleteById(undefined)).toBe(0)
expect(await dao.deleteById('123')).toBe(0)
Expand Down
99 changes: 10 additions & 89 deletions src/commondao/common.dao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
_truncate,
_typeCast,
_uniqBy,
AnyObject,
AppError,
AsyncMapper,
BaseDBEntity,
Expand Down Expand Up @@ -76,12 +75,8 @@ const isCI = !!process.env['CI']
* BM = Backend model (optimized for API access)
* TM = Transport model (optimized to be sent over the wire)
*/
export class CommonDao<
BM extends BaseDBEntity,
DBM extends BaseDBEntity = BM,
TM extends AnyObject = BM,
> {
constructor(public cfg: CommonDaoCfg<BM, DBM, TM>) {
export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
constructor(public cfg: CommonDaoCfg<BM, DBM>) {
this.cfg = {
// Default is to NOT log in AppEngine and in CI,
// otherwise to log Operations
Expand All @@ -99,11 +94,10 @@ export class CommonDao<
beforeDBMValidate: dbm => dbm,
beforeDBMToBM: dbm => dbm as any,
beforeBMToDBM: bm => bm as any,
beforeBMToTM: bm => bm as any,
anonymize: dbm => dbm,
onValidationError: err => err,
...cfg.hooks,
} satisfies Partial<CommonDaoHooks<BM, DBM, TM>>,
} satisfies Partial<CommonDaoHooks<BM, DBM>>,
}

if (this.cfg.generateId) {
Expand Down Expand Up @@ -178,28 +172,6 @@ export class CommonDao<
return dbm || null
}

async getByIdAsTM(id: undefined | null, opt?: CommonDaoOptions): Promise<null>
async getByIdAsTM(id?: string | null, opt?: CommonDaoOptions): Promise<TM | null>
async getByIdAsTM(id?: string | null, opt: CommonDaoOptions = {}): Promise<TM | null> {
if (!id) return null
const op = `getByIdAsTM(${id})`
const table = opt.table || this.cfg.table
const started = this.logStarted(op, table)
let [dbm] = await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id])
if (dbm && !opt.raw && this.cfg.hooks!.afterLoad) {
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
}

if (opt.raw) {
this.logResult(started, op, dbm, table)
return (dbm as any) || null
}
const bm = await this.dbmToBM(dbm, opt)
const tm = this.bmToTM(bm, opt)
this.logResult(started, op, tm, table)
return tm || null
}

async getByIds(ids: string[], opt: CommonDaoOptions = {}): Promise<BM[]> {
if (!ids.length) return []
const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
Expand Down Expand Up @@ -307,8 +279,8 @@ export class CommonDao<
/**
* Pass `table` to override table
*/
query(table?: string): RunnableDBQuery<BM, DBM, TM> {
return new RunnableDBQuery<BM, DBM, TM>(this, table)
query(table?: string): RunnableDBQuery<BM, DBM> {
return new RunnableDBQuery<BM, DBM>(this, table)
}

async runQuery(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<BM[]> {
Expand Down Expand Up @@ -385,35 +357,6 @@ export class CommonDao<
return { rows: dbms, ...queryResult }
}

async runQueryAsTM(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<TM[]> {
const { rows } = await this.runQueryExtendedAsTM(q, opt)
return rows
}

async runQueryExtendedAsTM(
q: DBQuery<DBM>,
opt: CommonDaoOptions = {},
): Promise<RunQueryResult<TM>> {
q.table = opt.table || q.table
const op = `runQueryAsTM(${q.pretty()})`
const started = this.logStarted(op, q.table)
let { rows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
if (!opt.raw && this.cfg.hooks!.afterLoad && rows.length) {
rows = (await pMap(rows, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
_isTruthy,
)
}

const partialQuery = !!q._selectedFieldNames
const tms =
partialQuery || opt.raw ? (rows as any[]) : this.bmsToTM(await this.dbmsToBM(rows, opt), opt)
this.logResult(started, op, tms, q.table)
return {
rows: tms,
...queryResult,
}
}

async runQueryCount(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
q.table = opt.table || q.table
const op = `runQueryCount(${q.pretty()})`
Expand Down Expand Up @@ -1239,28 +1182,6 @@ export class CommonDao<
return entities.map(entity => this.anyToDBM(entity, opt))
}

bmToTM(bm: undefined, opt?: CommonDaoOptions): TM | undefined
bmToTM(bm?: BM, opt?: CommonDaoOptions): TM
bmToTM(bm?: BM, opt?: CommonDaoOptions): TM | undefined {
if (bm === undefined) return

// optimization: 1 validation is enough
// Validate/convert BM
// bm gets assigned to the new reference
// bm = this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)

// BM > TM
const tm = this.cfg.hooks!.beforeBMToTM!(bm)

// Validate/convert DBM
return this.validateAndConvert(tm, this.cfg.tmSchema, DBModelType.TM, opt)
}

bmsToTM(bms: BM[], opt: CommonDaoOptions = {}): TM[] {
// try/catch?
return bms.map(bm => this.bmToTM(bm, opt))
}

/**
* Returns *converted value*.
* Validates (unless `skipValidation=true` passed).
Expand All @@ -1270,7 +1191,7 @@ export class CommonDao<
validateAndConvert<T>(
obj: Partial<T>,
schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined,
modelType: DBModelType,
modelType?: DBModelType,
opt: CommonDaoOptions = {},
): any {
// `raw` option completely bypasses any processing
Expand Down Expand Up @@ -1460,15 +1381,15 @@ export class CommonDaoTransaction {
}

async getById<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
dao: CommonDao<BM, DBM, any>,
dao: CommonDao<BM, DBM>,
id?: string | null,
opt?: CommonDaoOptions,
): Promise<BM | null> {
return await dao.getById(id, { ...opt, tx: this.tx })
}

async getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
dao: CommonDao<BM, DBM, any>,
dao: CommonDao<BM, DBM>,
ids: string[],
opt?: CommonDaoOptions,
): Promise<BM[]> {
Expand All @@ -1490,15 +1411,15 @@ export class CommonDaoTransaction {
// }

async save<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
dao: CommonDao<BM, DBM, any>,
dao: CommonDao<BM, DBM>,
bm: Unsaved<BM>,
opt?: CommonDaoSaveBatchOptions<DBM>,
): Promise<BM> {
return (await this.saveBatch(dao, [bm], opt))[0]!
}

async saveBatch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
dao: CommonDao<BM, DBM, any>,
dao: CommonDao<BM, DBM>,
bms: Unsaved<BM>[],
opt?: CommonDaoSaveBatchOptions<DBM>,
): Promise<BM[]> {
Expand Down
1 change: 0 additions & 1 deletion src/db.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ export enum DBRelation {
export enum DBModelType {
DBM = 'DBM',
BM = 'BM',
TM = 'TM',
}

/**
Expand Down
12 changes: 1 addition & 11 deletions src/query/dbQuery.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
AsyncMapper,
_truncate,
AnyObject,
_objectAssign,
BaseDBEntity,
ObjectWithId,
Expand Down Expand Up @@ -238,13 +237,12 @@ export class DBQuery<ROW extends ObjectWithId> {
export class RunnableDBQuery<
BM extends BaseDBEntity,
DBM extends BaseDBEntity = BM,
TM extends AnyObject = BM,
> extends DBQuery<DBM> {
/**
* Pass `table` to override table.
*/
constructor(
public dao: CommonDao<BM, DBM, TM>,
public dao: CommonDao<BM, DBM>,
table?: string,
) {
super(table || dao.cfg.table)
Expand All @@ -262,10 +260,6 @@ export class RunnableDBQuery<
return await this.dao.runQueryAsDBM(this, opt)
}

async runQueryAsTM(opt?: CommonDaoOptions): Promise<TM[]> {
return await this.dao.runQueryAsTM(this, opt)
}

async runQueryExtended(opt?: CommonDaoOptions): Promise<RunQueryResult<BM>> {
return await this.dao.runQueryExtended(this, opt)
}
Expand All @@ -274,10 +268,6 @@ export class RunnableDBQuery<
return await this.dao.runQueryExtendedAsDBM(this, opt)
}

async runQueryExtendedAsTM(opt?: CommonDaoOptions): Promise<RunQueryResult<TM>> {
return await this.dao.runQueryExtendedAsTM(this, opt)
}

async runQueryCount(opt?: CommonDaoOptions): Promise<number> {
return await this.dao.runQueryCount(this, opt)
}
Expand Down
2 changes: 0 additions & 2 deletions src/testing/daoTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
createTestItemsBM,
testItemBMSchema,
testItemDBMSchema,
testItemTMSchema,
TEST_TABLE,
createTestItemBM,
testItemDBMJsonSchema,
Expand All @@ -23,7 +22,6 @@ export function runCommonDaoTest(db: CommonDB, quirks: CommonDBImplementationQui
db,
dbmSchema: testItemDBMSchema,
bmSchema: testItemBMSchema,
tmSchema: testItemTMSchema,
logStarted: true,
logLevel: CommonDaoLogLevel.DATA_FULL,
})
Expand Down

0 comments on commit 8b36327

Please sign in to comment.