mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-07 16:30:49 +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 { ModelDb, TxDb } from '../memdb'
|
||||||
import { SortingOrder } from '../storage'
|
import { SortingOrder } from '../storage'
|
||||||
import { TxOperations } from '../tx'
|
import { TxOperations } from '../tx'
|
||||||
import { genMinModel } from './minmodel'
|
import { genMinModel, test, TestMixin } from './minmodel'
|
||||||
|
|
||||||
const txes = genMinModel()
|
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', () => {
|
describe('memdb', () => {
|
||||||
it('should save all tx', async () => {
|
it('should save all tx', async () => {
|
||||||
const hierarchy = new Hierarchy()
|
const hierarchy = new Hierarchy()
|
||||||
@ -34,21 +42,36 @@ describe('memdb', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should query model', async () => {
|
it('should query model', async () => {
|
||||||
const hierarchy = new Hierarchy()
|
const { model } = await createModel()
|
||||||
for (const tx of txes) await hierarchy.tx(tx)
|
|
||||||
const model = new ModelDb(hierarchy)
|
|
||||||
for (const tx of txes) await model.tx(tx)
|
|
||||||
const result = await model.findAll(core.class.Class, {})
|
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 })
|
const result2 = await model.findAll(core.class.Class, { _id: undefined })
|
||||||
expect(result2.length).toBe(0)
|
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 () => {
|
it('should allow delete', async () => {
|
||||||
const hierarchy = new Hierarchy()
|
const { model } = await createModel()
|
||||||
for (const tx of txes) await hierarchy.tx(tx)
|
|
||||||
const model = new ModelDb(hierarchy)
|
|
||||||
for (const tx of txes) await model.tx(tx)
|
|
||||||
const result = await model.findAll(core.class.Space, {})
|
const result = await model.findAll(core.class.Space, {})
|
||||||
expect(result.length).toBe(2)
|
expect(result.length).toBe(2)
|
||||||
|
|
||||||
|
@ -13,7 +13,9 @@
|
|||||||
// limitations under the License.
|
// 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 { ClassifierKind, DOMAIN_MODEL } from '../classes'
|
||||||
import type { Tx, TxCreateDoc } from '../tx'
|
import type { Tx, TxCreateDoc } from '../tx'
|
||||||
import core from '../component'
|
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)
|
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.
|
* Generate minimal model for testing purposes.
|
||||||
* @returns R
|
* @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.TxUpdateDoc, { extends: core.class.Tx, kind: ClassifierKind.CLASS }))
|
||||||
txes.push(createClass(core.class.TxRemoveDoc, { 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(
|
txes.push(
|
||||||
createDoc(core.class.Space, {
|
createDoc(core.class.Space, {
|
||||||
name: 'Sp1',
|
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> = {
|
const operators: Record<string, _OperatorFunc> = {
|
||||||
$push
|
$push,
|
||||||
|
$pushMixin
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +70,17 @@ export interface PushOptions<T extends Doc> {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @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
|
* @public
|
||||||
@ -178,6 +188,16 @@ export class TxOperations implements Storage {
|
|||||||
const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId)
|
const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId)
|
||||||
return this.storage.tx(tx)
|
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 type { Id, Plugin } from './platform'
|
||||||
import { PlatformError, Status, Severity } from './status'
|
import { PlatformError, Status, Severity } from './status'
|
||||||
import platform from './platform'
|
import platform, { _ID_SEPARATOR } from './platform'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -31,7 +31,7 @@ export interface _IdInfo {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function _parseId (id: Id): _IdInfo {
|
export function _parseId (id: Id): _IdInfo {
|
||||||
const path = id.split('.')
|
const path = id.split(_ID_SEPARATOR)
|
||||||
if (path.length !== 3) {
|
if (path.length !== 3) {
|
||||||
throw new PlatformError(
|
throw new PlatformError(
|
||||||
new Status(Severity.ERROR, platform.status.InvalidId, { id })
|
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>>
|
export type Namespace = Record<string, Record<string, string>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export const _ID_SEPARATOR = ':'
|
||||||
|
|
||||||
function identify (
|
function identify (
|
||||||
result: Record<string, any>,
|
result: Record<string, any>,
|
||||||
prefix: string,
|
prefix: string,
|
||||||
@ -73,7 +78,7 @@ function identify (
|
|||||||
if (typeof result[key] === 'string') {
|
if (typeof result[key] === 'string') {
|
||||||
throw new Error(`'identify' overwrites '${key}'.`)
|
throw new Error(`'identify' overwrites '${key}'.`)
|
||||||
}
|
}
|
||||||
const ident = prefix + '.' + key
|
const ident = prefix + _ID_SEPARATOR + key
|
||||||
result[key] =
|
result[key] =
|
||||||
typeof value === 'string'
|
typeof value === 'string'
|
||||||
? ident
|
? ident
|
||||||
|
Loading…
Reference in New Issue
Block a user