mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-29 19:56:18 +00:00
initial $pushMixin
implementation
Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
parent
f8a44dba73
commit
e25d9ed618
@ -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)
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 })
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user