Skip to content
Draft
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions packages/core/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export class Database<S = {}, N = {}, C extends Context = Context> extends Servi
}

extend<K extends Keys<S>>(name: K, fields: Field.Extension<S[K], N>, config: Partial<Model.Config<FlatKeys<S[K]>>> = {}) {
const disposables: (() => void)[] = []
let model = this.tables[name]
if (!model) {
model = this.tables[name] = new Model(name)
Expand All @@ -142,7 +143,7 @@ export class Database<S = {}, N = {}, C extends Context = Context> extends Servi
this.parseField(field, transformer, undefined, value => field = fields[key] = value)
if (typeof field === 'object') field.transformers = transformer
})
model.extend(fields, config)
disposables.push(model.extend(fields, config))
if (makeArray(model.primary).every(key => key in fields)) {
defineProperty(model, 'ctx', this.ctx)
}
Expand All @@ -159,6 +160,7 @@ export class Database<S = {}, N = {}, C extends Context = Context> extends Servi

if (relation.type === 'oneToOne' || relation.type === 'manyToOne') {
relation.fields.forEach((x, i) => {
if (!(x in model.fields)) disposables.unshift(() => delete model.fields[x])
model.fields[x] ??= { ...relmodel.fields[relation.references[i]] } as any
if (!relation.required) {
model.fields[x]!.nullable = true
Expand All @@ -171,7 +173,7 @@ export class Database<S = {}, N = {}, C extends Context = Context> extends Servi
const shared = Object.entries(relation.shared).map(([x, y]) => [Relation.buildSharedKey(x, y), model.fields[x]!.deftype] as const)
const fields = relation.fields.map(x => [Relation.buildAssociationKey(x, name), model.fields[x]!.deftype] as const)
const references = relation.references.map(x => [Relation.buildAssociationKey(x, relation.table), relmodel.fields[x]?.deftype] as const)
this.extend(assocTable as any, {
disposables.push(this.extend(assocTable as any, {
...Object.fromEntries([...shared, ...fields, ...references]),
[name]: {
type: 'manyToOne',
Expand All @@ -187,7 +189,7 @@ export class Database<S = {}, N = {}, C extends Context = Context> extends Servi
},
} as any, {
primary: [...shared, ...fields, ...references].map(x => x[0]) as any,
})
}))
}
})
// use relation field as primary
Expand All @@ -199,6 +201,10 @@ export class Database<S = {}, N = {}, C extends Context = Context> extends Servi

this.prepareTasks[name] = this.prepare(name)
;(this.ctx as Context).emit('model', name)
return this.ctx.effect(() => () => {
disposables.splice(0).forEach(dispose => dispose())
;(this.ctx as Context).emit('model', name)
})
}

private _parseField(field: any, transformers: Driver.Transformer[] = [], setInitial?: (value) => void, setField?: (value) => void): Type {
Expand Down Expand Up @@ -304,7 +310,7 @@ export class Database<S = {}, N = {}, C extends Context = Context> extends Servi
fields: Field.Extension<S[K], N>,
callback: Model.Migration<this>,
) {
this.extend(name, fields, { callback })
return this.extend(name, fields, { callback })
}

select<T>(table: Selection<T>, query?: Query<T>): Selection<T>
Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export class Model<S = any> {
this.foreign = {}
}

extend(fields: Field.Extension<S>, config?: Partial<Model.Config>): void
extend(fields: Field.Extension<S>, config?: Partial<Model.Config>): () => void
extend(fields = {}, config: Partial<Model.Config> = {}) {
const { primary, autoInc, unique = [], indexes = [], foreign, callback } = config

Expand All @@ -285,6 +285,7 @@ export class Model<S = any> {
if (callback) this.migrations.set(callback, Object.keys(fields))

for (const key in fields) {
if (Object.keys(this.fields).includes(key)) throw new TypeError(`field "${key}" already exists in table "${this.name}"`)
this.fields[key] = Field.parse(fields[key])
this.fields[key].deprecated = !!callback
}
Expand All @@ -297,6 +298,17 @@ export class Model<S = any> {
this.checkIndex(this.primary)
this.unique.forEach(index => this.checkIndex(index))
this.indexes.forEach(index => this.checkIndex(index))

if (Object.keys(fields).some(key => makeArray(this.primary).includes(key))) {
return () => {
const database = this.ctx?.get('model')
delete database?.tables[this.name]
delete database?.migrateTasks[this.name]
delete database?.['prepareTasks'][this.name]
}
} else {
return () => Object.keys(fields).forEach(key => delete this.fields[key])
}
}

private parseIndex(index: MaybeArray<string> | Driver.Index): Driver.Index {
Expand Down
3 changes: 2 additions & 1 deletion packages/mongo/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Database } from 'minato'
import MongoDriver from '@minatojs/driver-mongo'
import test from '@minatojs/tests'
import Logger from 'reggol'
import { noop } from 'cosmokit'

const logger = new Logger('mongo')

Expand All @@ -19,7 +20,7 @@ describe('@minatojs/driver-mongo', () => {
})

after(async () => {
await database.dropAll()
await database.dropAll().catch(noop)
await database.stopAll()
logger.level = 2
})
Expand Down
28 changes: 23 additions & 5 deletions packages/tests/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Context, ForkScope } from 'cordis'
import { Database } from 'minato'
import ModelOperations from './model'
import QueryOperators from './query'
Expand Down Expand Up @@ -32,9 +33,20 @@ function setValue(obj: any, path: string, value: any) {
}
}

function createUnit<T>(target: T, root = false): Unit<T> {
function createUnit<T>(target: T, level = 0): Unit<T> {
const title = target['name']
const test: any = (database: Database, options: any = {}) => {
function callback() {
let fork: ForkScope<Context> | undefined
if (level === 1) {
fork = database['ctx'].plugin({
inject: ['model'],
name: title,
apply: () => {},
})
database = fork.ctx.model
}

if (typeof target === 'function') {
target(database, options)
}
Expand All @@ -43,13 +55,19 @@ function createUnit<T>(target: T, root = false): Unit<T> {
if (options[key] === false || Keywords.includes(key)) continue
test[key](database, options[key])
}

if (fork) {
after(async () => {
await database.dropAll()
fork.dispose()
})
}
}

process.argv.filter(x => x.startsWith('--+')).forEach(x => setValue(options, x.slice(3), true))
process.argv.filter(x => x.startsWith('---')).forEach(x => setValue(options, x.slice(3), false))

const title = target['name']
if (!root && title) {
if (level && title) {
describe(title.replace(/(?=[A-Z])/g, ' ').trimStart(), callback)
} else {
callback()
Expand All @@ -58,7 +76,7 @@ function createUnit<T>(target: T, root = false): Unit<T> {

for (const key in target) {
if (Keywords.includes(key)) continue
test[key] = createUnit(target[key])
test[key] = createUnit(target[key], level + 1)
}

return test
Expand All @@ -76,4 +94,4 @@ namespace Tests {
export const relation = Relation
}

export default createUnit(Tests, true)
export default createUnit(Tests)
46 changes: 23 additions & 23 deletions packages/tests/src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,32 +37,32 @@ interface Tables {
}

function JsonTests(database: Database<Tables>) {
database.extend('foo', {
id: 'unsigned',
value: 'integer',
})
before(async () => {
database.extend('foo', {
id: 'unsigned',
value: 'integer',
})

database.extend('bar', {
id: 'unsigned',
uid: 'unsigned',
pid: 'unsigned',
value: 'integer',
obj: 'json',
s: 'string',
l: 'list',
}, {
autoInc: true,
})
database.extend('bar', {
id: 'unsigned',
uid: 'unsigned',
pid: 'unsigned',
value: 'integer',
obj: 'json',
s: 'string',
l: 'list',
}, {
autoInc: true,
})

database.extend('baz', {
id: 'unsigned',
nums: {
type: 'array',
inner: 'unsigned',
}
})
database.extend('baz', {
id: 'unsigned',
nums: {
type: 'array',
inner: 'unsigned',
}
})

before(async () => {
await setup(database, 'foo', [
{ id: 1, value: 0 },
{ id: 2, value: 2 },
Expand Down
47 changes: 41 additions & 6 deletions packages/tests/src/migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ function MigrationTests(database: Database<Tables>) {
])

database.extend('qux', {
id: 'unsigned',
text: 'string(64)',
number: 'unsigned',
})

Expand All @@ -73,6 +71,46 @@ function MigrationTests(database: Database<Tables>) {
])
})

it('alter disposable field', async () => {
Reflect.deleteProperty(database.tables, 'qux')

database.extend('qux', {
id: 'unsigned',
text: 'string(64)',
})

await database.upsert('qux', [
{ id: 1, text: 'foo' },
{ id: 2, text: 'bar' },
])

await expect(database.get('qux', {})).to.eventually.deep.equal([
{ id: 1, text: 'foo' },
{ id: 2, text: 'bar' },
])

const dispose = database.extend('qux', {
number: 'unsigned',
})

await database.upsert('qux', [
{ id: 1, text: 'foo', number: 100 },
{ id: 2, text: 'bar', number: 200 },
])

await expect(database.get('qux', {})).to.eventually.deep.equal([
{ id: 1, text: 'foo', number: 100 },
{ id: 2, text: 'bar', number: 200 },
])

dispose()

await expect(database.get('qux', {})).to.eventually.deep.equal([
{ id: 1, text: 'foo' },
{ id: 2, text: 'bar' },
])
})

it('should migrate field', async () => {
Reflect.deleteProperty(database.tables, 'qux')

Expand Down Expand Up @@ -174,10 +212,7 @@ function MigrationTests(database: Database<Tables>) {
{ id: 2, number: 2 },
])

database.extend('qux', {
id: 'unsigned',
number: 'unsigned',
}, {
database.extend('qux', {}, {
indexes: ['number'],
})

Expand Down
Loading