initial $pushMixin implementation

Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
Andrey Platov 2021-08-05 11:15:09 +02:00
parent f8a44dba73
commit e25d9ed618
No known key found for this signature in database
GPG Key ID: C8787EFEB4B64AF0
6 changed files with 95 additions and 16 deletions

View File

@ -19,10 +19,18 @@ import { Hierarchy } from '../hierarchy'
import { ModelDb, TxDb } from '../memdb'
import { SortingOrder } from '../storage'
import { TxOperations } from '../tx'
import { genMinModel } from './minmodel'
import { genMinModel, test, TestMixin } from './minmodel'
const txes = genMinModel()
async function createModel (): Promise<{ model: ModelDb, hierarchy: Hierarchy }> {
const hierarchy = new Hierarchy()
for (const tx of txes) await hierarchy.tx(tx)
const model = new ModelDb(hierarchy)
for (const tx of txes) await model.tx(tx)
return { model, hierarchy }
}
describe('memdb', () => {
it('should save all tx', async () => {
const hierarchy = new Hierarchy()
@ -34,21 +42,36 @@ describe('memdb', () => {
})
it('should query model', async () => {
const hierarchy = new Hierarchy()
for (const tx of txes) await hierarchy.tx(tx)
const model = new ModelDb(hierarchy)
for (const tx of txes) await model.tx(tx)
const { model } = await createModel()
const result = await model.findAll(core.class.Class, {})
expect(result.length).toBe(9)
expect(result.length).toBe(10)
const result2 = await model.findAll(core.class.Class, { _id: undefined })
expect(result2.length).toBe(0)
})
it('should create mixin', async () => {
const { model } = await createModel()
const ops = new TxOperations(model, core.account.System)
await ops.createMixin(core.class.Obj, core.class.Class, test.mixin.TestMixin, { arr: ['hello'] })
const objClass = (await model.findAll(core.class.Class, { _id: core.class.Obj }))[0] as any
expect(objClass['test:mixin:TestMixin'].arr).toEqual(expect.arrayContaining(['hello']))
await ops.updateDoc(test.mixin.TestMixin, core.space.Model, core.class.Obj as unknown as Ref<TestMixin>, {
$pushMixin: {
$mixin: test.mixin.TestMixin,
values: {
arr: 'there'
}
}
})
const objClass2 = (await model.findAll(core.class.Class, { _id: core.class.Obj }))[0] as any
expect(objClass2['test:mixin:TestMixin'].arr).toEqual(expect.arrayContaining(['hello', 'there']))
})
it('should allow delete', async () => {
const hierarchy = new Hierarchy()
for (const tx of txes) await hierarchy.tx(tx)
const model = new ModelDb(hierarchy)
for (const tx of txes) await model.tx(tx)
const { model } = await createModel()
const result = await model.findAll(core.class.Space, {})
expect(result.length).toBe(2)

View File

@ -13,7 +13,9 @@
// limitations under the License.
//
import type { Class, Data, Doc, Obj, Ref } from '../classes'
import type { Plugin } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import type { Class, Data, Doc, Obj, Ref, Mixin, Arr } from '../classes'
import { ClassifierKind, DOMAIN_MODEL } from '../classes'
import type { Tx, TxCreateDoc } from '../tx'
import core from '../component'
@ -29,6 +31,16 @@ export function createDoc<T extends Doc> (_class: Ref<Class<T>>, attributes: Dat
return txFactory.createTxCreateDoc(_class, core.space.Model, attributes)
}
export interface TestMixin extends Doc {
arr: Arr<string>
}
export const test = plugin('test' as Plugin, {
mixin: {
TestMixin: '' as Ref<Mixin<TestMixin>>
}
})
/**
* Generate minimal model for testing purposes.
* @returns R
@ -47,6 +59,8 @@ export function genMinModel (): Tx[] {
txes.push(createClass(core.class.TxUpdateDoc, { extends: core.class.Tx, kind: ClassifierKind.CLASS }))
txes.push(createClass(core.class.TxRemoveDoc, { extends: core.class.Tx, kind: ClassifierKind.CLASS }))
txes.push(createClass(test.mixin.TestMixin, { extends: core.class.Doc, kind: ClassifierKind.MIXIN }))
txes.push(
createDoc(core.class.Space, {
name: 'Sp1',

View File

@ -33,8 +33,25 @@ function $push (document: Doc, keyval: Record<string, PropertyType>): void {
}
}
function $pushMixin (document: Doc, options: any): void {
const doc = document as any
const mixinId = options.$mixin
if (mixinId === undefined) { throw new Error('$mixin must be specified for $push_mixin operation') }
const mixin = doc[mixinId]
const keyval = options.values
for (const key in keyval) {
const arr = mixin[key]
if (arr === undefined) {
mixin[key] = [keyval[key]]
} else {
arr.push(keyval[key])
}
}
}
const operators: Record<string, _OperatorFunc> = {
$push
$push,
$pushMixin
}
/**

View File

@ -70,7 +70,17 @@ export interface PushOptions<T extends Doc> {
/**
* @public
*/
export type DocumentUpdate<T extends Doc> = Partial<Data<T>> & PushOptions<T>
export interface PushMixinOptions<D extends Doc> {
$pushMixin?: {
$mixin: Ref<Mixin<D>>
values: Partial<OmitNever<ArrayAsElement<D>>>
}
}
/**
* @public
*/
export type DocumentUpdate<T extends Doc> = Partial<Data<T>> & PushOptions<T> & PushMixinOptions<T>
/**
* @public
@ -178,6 +188,16 @@ export class TxOperations implements Storage {
const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId)
return this.storage.tx(tx)
}
createMixin<D extends Doc, M extends D>(
objectId: Ref<D>,
objectClass: Ref<Class<D>>,
mixin: Ref<Mixin<M>>,
attributes: ExtendedAttributes<D, M>
): Promise<void> {
const tx = this.txFactory.createTxMixin(objectId, objectClass, mixin, attributes)
return this.storage.tx(tx)
}
}
/**

View File

@ -16,7 +16,7 @@
import type { Id, Plugin } from './platform'
import { PlatformError, Status, Severity } from './status'
import platform from './platform'
import platform, { _ID_SEPARATOR } from './platform'
/**
* @internal
@ -31,7 +31,7 @@ export interface _IdInfo {
* @internal
*/
export function _parseId (id: Id): _IdInfo {
const path = id.split('.')
const path = id.split(_ID_SEPARATOR)
if (path.length !== 3) {
throw new PlatformError(
new Status(Severity.ERROR, platform.status.InvalidId, { id })

View File

@ -63,6 +63,11 @@ export type StatusCode<T extends Record<string, any> = {}> = IntlString<T>
*/
export type Namespace = Record<string, Record<string, string>>
/**
* @internal
*/
export const _ID_SEPARATOR = ':'
function identify (
result: Record<string, any>,
prefix: string,
@ -73,7 +78,7 @@ function identify (
if (typeof result[key] === 'string') {
throw new Error(`'identify' overwrites '${key}'.`)
}
const ident = prefix + '.' + key
const ident = prefix + _ID_SEPARATOR + key
result[key] =
typeof value === 'string'
? ident