Skip to content

Commit e1e0ca6

Browse files
committed
UBERF-9747
Signed-off-by: Andrey Sobolev <[email protected]>
1 parent a98b83d commit e1e0ca6

File tree

36 files changed

+1227
-1115
lines changed

36 files changed

+1227
-1115
lines changed

.vscode/launch.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@
103103
"UPLOAD_URL": "/files",
104104
"AI_BOT_URL": "http://localhost:4010",
105105
"STATS_URL": "http://huly.local:4900",
106-
"QUEUE_CONFIG": "localhost:19092"
106+
"QUEUE_CONFIG": "localhost:19092",
107+
"RATE_LIMIT_MAX": "25000"
107108
},
108109
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
109110
"runtimeVersion": "20",

models/core/src/tx.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ export class TTx extends TDoc implements Tx {
4949
objectSpace!: Ref<Space>
5050
}
5151

52-
@Model(core.class.TxModelUpgrade, core.class.Tx, DOMAIN_TX)
53-
export class TTxModelUpgrade extends TTx {}
54-
5552
@Model(core.class.TxCUD, core.class.Tx)
5653
export class TTxCUD<T extends Doc> extends TTx implements TxCUD<T> {
5754
@Prop(TypeRef(core.class.Doc), core.string.Object)

packages/core/src/__tests__/client.test.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
// limitations under the License.
1515
//
1616
import { IntlString, Plugin } from '@hcengineering/platform'
17-
import { ClientConnectEvent, DocChunk } from '..'
18-
import type { Class, Data, Doc, Domain, PluginConfiguration, Ref, Timestamp } from '../classes'
17+
import { ClientConnectEvent, DocChunk, type WorkspaceUuid } from '..'
18+
import type { Account, Class, Data, Doc, Domain, PluginConfiguration, Ref, Timestamp } from '../classes'
1919
import { ClassifierKind, DOMAIN_MODEL, Space } from '../classes'
2020
import { ClientConnection, createClient } from '../client'
2121
import core from '../component'
@@ -104,21 +104,25 @@ describe('client', () => {
104104
}
105105

106106
return new (class implements ClientConnection {
107-
handler?: (event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>
107+
handler?: (event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>
108108

109109
set onConnect (
110-
handler: ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>) | undefined
110+
handler: ((event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>) | undefined
111111
) {
112112
this.handler = handler
113-
void this.handler?.(ClientConnectEvent.Connected, '', {})
113+
void this.handler?.(ClientConnectEvent.Connected, {}, {})
114114
}
115115

116116
get onConnect ():
117-
| ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>)
117+
| ((event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>)
118118
| undefined {
119119
return this.handler
120120
}
121121

122+
getAccount (): Promise<Account > {
123+
throw new Error('Method not implemented.')
124+
}
125+
122126
isConnected = (): boolean => true
123127
findAll = findAll
124128
pushHandler = (): void => {}

packages/core/src/__tests__/connection.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
// limitations under the License.
1414
//
1515

16-
import { ClientConnectEvent, DocChunk, generateId } from '..'
17-
import type { Class, Doc, Domain, Ref, Timestamp } from '../classes'
16+
import { ClientConnectEvent, DocChunk, generateId, type WorkspaceUuid } from '..'
17+
import type { Account, Class, Doc, Domain, Ref, Timestamp } from '../classes'
1818
import { ClientConnection } from '../client'
1919
import core from '../component'
2020
import { Hierarchy } from '../hierarchy'
@@ -47,19 +47,23 @@ export async function connect (handler: (tx: Tx) => void): Promise<ClientConnect
4747
isConnected = (): boolean => true
4848
findAll = findAll
4949

50-
handler?: (event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>
50+
handler?: (event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>
5151

5252
set onConnect (
53-
handler: ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>) | undefined
53+
handler: ((event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>) | undefined
5454
) {
5555
this.handler = handler
56-
void this.handler?.(ClientConnectEvent.Connected, '', {})
56+
void this.handler?.(ClientConnectEvent.Connected, {}, {})
5757
}
5858

59-
get onConnect (): ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>) | undefined {
59+
get onConnect (): ((event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>) | undefined {
6060
return this.handler
6161
}
6262

63+
getAccount (): Promise<Account> {
64+
throw new Error('Method not implemented.')
65+
}
66+
6367
async searchFulltext (query: SearchQuery, options: SearchOptions): Promise<SearchResult> {
6468
return { docs: [] }
6569
}

packages/core/src/classes.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ export interface Obj {
7171

7272
export interface Account {
7373
uuid: AccountUuid
74-
role: AccountRole
74+
roles: Record<WorkspaceUuid, AccountRole>
75+
76+
role: AccountRole // Role in current workspace
77+
targetWorkspace: WorkspaceUuid // In case target workspace is used, it will be filled with personal workspace UUID in case of all workspaces mode.
7578
primarySocialId: PersonId
7679
socialIds: PersonId[]
7780
fullSocialIds: SocialId[]
@@ -521,7 +524,8 @@ export enum AccountRole {
521524
Guest = 'GUEST',
522525
User = 'USER',
523526
Maintainer = 'MAINTAINER',
524-
Owner = 'OWNER'
527+
Owner = 'OWNER',
528+
Undetermined = 'UNDETERMINED' // In case of multi workspaces mode
525529
}
526530

527531
/**
@@ -530,6 +534,7 @@ export enum AccountRole {
530534
export const roleOrder: Record<AccountRole, number> = {
531535
[AccountRole.DocGuest]: 10,
532536
[AccountRole.Guest]: 20,
537+
[AccountRole.Undetermined]: 25,
533538
[AccountRole.User]: 30,
534539
[AccountRole.Maintainer]: 40,
535540
[AccountRole.Owner]: 50

packages/core/src/client.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@
1515

1616
import { Analytics } from '@hcengineering/analytics'
1717
import { BackupClient, DocChunk } from './backup'
18-
import { Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp } from './classes'
18+
import { Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp, type Account } from './classes'
1919
import core from './component'
2020
import { Hierarchy } from './hierarchy'
2121
import { MeasureContext, MeasureMetricsContext } from './measurements'
2222
import { ModelDb } from './memdb'
2323
import type { DocumentQuery, FindOptions, FindResult, FulltextStorage, Storage, TxResult, WithLookup } from './storage'
2424
import { SearchOptions, SearchQuery, SearchResult } from './storage'
2525
import { Tx, TxCUD, WorkspaceEvent, type TxWorkspaceEvent } from './tx'
26-
import { platformNow, platformNowDiff, toFindResult } from './utils'
26+
import { platformNow, platformNowDiff, toFindResult, type WorkspaceUuid } from './utils'
27+
import { deepEqual } from 'fast-equals'
2728

2829
/**
2930
* @public
@@ -80,13 +81,15 @@ export interface ClientConnection extends Storage, FulltextStorage, BackupClient
8081
isConnected: () => boolean
8182

8283
close: () => Promise<void>
83-
onConnect?: (event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>
84+
onConnect?: (event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>
8485

8586
// If hash is passed, will return LoadModelResponse
8687
loadModel: (last: Timestamp, hash?: string) => Promise<Tx[] | LoadModelResponse>
8788

88-
getLastHash?: (ctx: MeasureContext) => Promise<string | undefined>
89+
getLastHash?: (ctx: MeasureContext) => Promise<Record<WorkspaceUuid, string | undefined>>
8990
pushHandler: (handler: Handler) => void
91+
92+
getAccount: () => Promise<Account>
9093
}
9194

9295
class ClientImpl implements Client, BackupClient {
@@ -237,7 +240,7 @@ export async function createClient (
237240
let hierarchy = new Hierarchy()
238241
let model = new ModelDb(hierarchy)
239242

240-
let lastTx: string | undefined
243+
let lastTx: Record<WorkspaceUuid, string | undefined> | undefined
241244

242245
function txHandler (...tx: Tx[]): void {
243246
if (tx == null || tx.length === 0) {
@@ -282,7 +285,7 @@ export async function createClient (
282285
txBuffer = undefined
283286

284287
const oldOnConnect:
285-
| ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>)
288+
| ((event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>)
286289
| undefined = conn.onConnect
287290
conn.onConnect = async (event, _lastTx, data) => {
288291
console.log('Client: onConnect', event)
@@ -324,7 +327,7 @@ export async function createClient (
324327
return
325328
}
326329

327-
if (lastTx === _lastTx) {
330+
if (deepEqual(lastTx, _lastTx)) {
328331
// Same lastTx, no need to refresh
329332
await oldOnConnect?.(ClientConnectEvent.Reconnected, _lastTx, data)
330333
return
@@ -362,9 +365,13 @@ async function loadModel (
362365
hash: ''
363366
}
364367

365-
if (conn.getLastHash !== undefined && (await conn.getLastHash(ctx)) === current.hash) {
366-
// We have same model hash.
367-
return { mode: 'same', current: current.transactions, addition: [] }
368+
if (conn.getLastHash !== undefined) {
369+
const lastHash = await conn.getLastHash(ctx)
370+
const account = await conn.getAccount()
371+
if (lastHash[account.targetWorkspace] === current.hash) {
372+
// We have same model hash.
373+
return { mode: 'same', current: current.transactions, addition: [] }
374+
}
368375
}
369376
const lastTxTime = getLastTxTime(current.transactions)
370377
const result = await ctx.with('connection-load-model', { hash: current.hash !== '' }, (ctx) =>

packages/core/src/component.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
import type { Asset, IntlString, Metadata, Plugin, StatusCode } from '@hcengineering/platform'
1616
import { plugin } from '@hcengineering/platform'
1717
import type { BenchmarkDoc } from './benchmark'
18-
import { AccountRole } from './classes'
1918
import type {
2019
Account,
20+
AccountUuid,
2121
AnyAttribute,
2222
ArrOf,
2323
Association,
@@ -59,21 +59,21 @@ import type {
5959
TypeAny,
6060
TypedSpace,
6161
UserStatus,
62-
Version,
63-
AccountUuid
62+
Version
6463
} from './classes'
64+
import { AccountRole } from './classes'
6565
import { Status, StatusCategory } from './status'
6666
import type {
6767
Tx,
6868
TxApplyIf,
6969
TxCUD,
7070
TxCreateDoc,
7171
TxMixin,
72-
TxModelUpgrade,
7372
TxRemoveDoc,
7473
TxUpdateDoc,
7574
TxWorkspaceEvent
7675
} from './tx'
76+
import type { WorkspaceUuid } from './utils'
7777

7878
/**
7979
* @public
@@ -88,7 +88,9 @@ export const systemAccountEmail = '[email protected]'
8888
export const systemAccountUuid = '1749089e-22e6-48de-af4e-165e18fbd2f9' as AccountUuid
8989
export const systemAccount: Account = {
9090
uuid: systemAccountUuid,
91+
roles: { },
9192
role: AccountRole.Owner,
93+
targetWorkspace: systemAccountUuid as any as WorkspaceUuid,
9294
primarySocialId: '' as PersonId,
9395
socialIds: [],
9496
fullSocialIds: []
@@ -107,7 +109,6 @@ export default plugin(coreId, {
107109
Interface: '' as Ref<Class<Interface<Doc>>>,
108110
Attribute: '' as Ref<Class<AnyAttribute>>,
109111
Tx: '' as Ref<Class<Tx>>,
110-
TxModelUpgrade: '' as Ref<Class<TxModelUpgrade>>,
111112
TxWorkspaceEvent: '' as Ref<Class<TxWorkspaceEvent>>,
112113
TxApplyIf: '' as Ref<Class<TxApplyIf>>,
113114
TxCUD: '' as Ref<Class<TxCUD<Doc>>>,

packages/core/src/tx.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { setObjectValue } from './objvalue'
3434
import { _getOperator } from './operator'
3535
import { _toDoc } from './proxy'
3636
import type { DocumentQuery, TxResult } from './storage'
37-
import { generateId } from './utils'
37+
import { generateId, type WorkspaceUuid } from './utils'
3838

3939
/**
4040
* @public
@@ -52,7 +52,9 @@ export enum WorkspaceEvent {
5252
SecurityChange,
5353
MaintenanceNotification,
5454
BulkUpdate,
55-
LastTx
55+
LastTx,
56+
WorkpaceActive,
57+
ModelUpgrade = 7
5658
}
5759

5860
/**
@@ -62,6 +64,7 @@ export enum WorkspaceEvent {
6264
export interface TxWorkspaceEvent<T = any> extends Tx {
6365
event: WorkspaceEvent
6466
params: T
67+
workspace?: WorkspaceUuid
6568
}
6669

6770
/**
@@ -78,11 +81,6 @@ export interface BulkUpdateEvent {
7881
_class: Ref<Class<Doc>>[]
7982
}
8083

81-
/**
82-
* @public
83-
*/
84-
export interface TxModelUpgrade extends Tx {}
85-
8684
/**
8785
* @public
8886
*/

packages/core/src/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,10 @@ export function isOwnerOrMaintainer (): boolean {
762762
}
763763

764764
export function hasAccountRole (acc: Account, targerRole: AccountRole): boolean {
765-
return roleOrder[acc.role] >= roleOrder[targerRole]
765+
if (acc.targetWorkspace == null) {
766+
throw new Error('Account has no target workspace')
767+
}
768+
return roleOrder[acc.roles[acc.targetWorkspace]] >= roleOrder[targerRole]
766769
}
767770

768771
export function getBranding (brandings: BrandingMap, key: string | undefined): Branding | null {

packages/query/src/__tests__/connection.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ import core, {
3939
Timestamp,
4040
Tx,
4141
TxDb,
42-
TxResult
42+
TxResult,
43+
type Account,
44+
type WorkspaceUuid
4345
} from '@hcengineering/core'
4446
import { genMinModel } from './minmodel'
4547

@@ -50,6 +52,7 @@ FulltextStorage & {
5052
isConnected: () => boolean
5153
loadModel: (last: Timestamp, hash?: string) => Promise<Tx[] | LoadModelResponse>
5254
pushHandler: (handler: Handler) => void
55+
getAccount: () => Promise<Account>
5356
}
5457
> {
5558
const txes = genMinModel()
@@ -150,18 +153,22 @@ FulltextStorage & {
150153

151154
async sendForceClose (): Promise<void> {}
152155

153-
handler?: (event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>
156+
handler?: (event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>
154157

155158
set onConnect (
156-
handler: ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>) | undefined
159+
handler: ((event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>) | undefined
157160
) {
158161
this.handler = handler
159-
void this.handler?.(ClientConnectEvent.Connected, '', {})
162+
void this.handler?.(ClientConnectEvent.Connected, {}, {})
160163
}
161164

162-
get onConnect (): ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise<void>) | undefined {
165+
get onConnect (): ((event: ClientConnectEvent, lastTx: Record<WorkspaceUuid, string | undefined> | undefined, data: any) => Promise<void>) | undefined {
163166
return this.handler
164167
}
168+
169+
getAccount (): Promise<Account> {
170+
throw new Error('Method not implemented.')
171+
}
165172
}
166173

167174
return new TestConnection(hierarchy, model, transactions)

0 commit comments

Comments
 (0)