platform/server/core/src/utils.ts
Andrey Sobolev 4eac1927f0
UBERF-7532: Bulk operations for triggers (#6023)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
2024-07-09 00:04:05 +07:00

204 lines
4.8 KiB
TypeScript

import core, {
WorkspaceEvent,
generateId,
getTypeOf,
type BulkUpdateEvent,
type Class,
type Doc,
type FullParamsType,
type MeasureContext,
type ParamsType,
type Ref,
type TxWorkspaceEvent,
type WorkspaceIdWithUrl,
type Branding,
type BrandingMap
} from '@hcengineering/core'
import { type Hash } from 'crypto'
import fs from 'fs'
import type { SessionContext } from './types'
/**
* Return some estimation for object size
*/
export function estimateDocSize (_obj: any): number {
let result = 0
const toProcess = [_obj]
while (toProcess.length > 0) {
const obj = toProcess.shift()
if (typeof obj === 'undefined') {
continue
}
if (typeof obj === 'function') {
continue
}
for (const key in obj) {
// include prototype properties
const value = obj[key]
const type = getTypeOf(value)
result += key.length
switch (type) {
case 'Array':
result += 4
toProcess.push(value)
break
case 'Object':
toProcess.push(value)
break
case 'Date':
result += 24 // Some value
break
case 'string':
result += (value as string).length
break
case 'number':
result += 8
break
case 'boolean':
result += 1
break
case 'symbol':
result += (value as symbol).toString().length
break
case 'bigint':
result += (value as bigint).toString().length
break
case 'undefined':
result += 1
break
case 'null':
result += 1
break
default:
result += value.toString().length
}
}
}
return result
}
/**
* Return some estimation for object size
*/
export function updateHashForDoc (hash: Hash, _obj: any): void {
const toProcess = [_obj]
while (toProcess.length > 0) {
const obj = toProcess.shift()
if (typeof obj === 'undefined') {
continue
}
if (typeof obj === 'function') {
continue
}
for (const key in obj) {
// include prototype properties
const value = obj[key]
const type = getTypeOf(value)
hash.update(key)
switch (type) {
case 'Array':
toProcess.push(value)
break
case 'Object':
toProcess.push(value)
break
case 'Date':
hash.update(value.toString())
break
case 'string':
hash.update(value)
break
case 'number':
hash.update((value as number).toString(16))
break
case 'boolean':
hash.update((value as boolean) ? '1' : '0')
break
case 'symbol':
hash.update((value as symbol).toString())
break
case 'bigint':
hash.update((value as bigint).toString())
break
case 'undefined':
hash.update('und')
break
case 'null':
hash.update('null')
break
default:
hash.update(value.toString())
}
}
}
}
export class SessionContextImpl implements SessionContext {
constructor (
readonly ctx: MeasureContext,
readonly userEmail: string,
readonly sessionId: string,
readonly admin: boolean | undefined,
readonly derived: SessionContext['derived'],
readonly workspace: WorkspaceIdWithUrl,
readonly branding: Branding | null,
readonly isAsyncContext: boolean
) {}
with<T>(
name: string,
params: ParamsType,
op: (ctx: SessionContext) => T | Promise<T>,
fullParams?: FullParamsType
): Promise<T> {
return this.ctx.with(
name,
params,
async (ctx) =>
await op(
new SessionContextImpl(
ctx,
this.userEmail,
this.sessionId,
this.admin,
this.derived,
this.workspace,
this.branding,
this.isAsyncContext
)
),
fullParams
)
}
}
export function createBroadcastEvent (classes: Ref<Class<Doc>>[]): TxWorkspaceEvent<BulkUpdateEvent> {
return {
_class: core.class.TxWorkspaceEvent,
_id: generateId(),
event: WorkspaceEvent.BulkUpdate,
params: {
_class: classes
},
modifiedBy: core.account.System,
modifiedOn: Date.now(),
objectSpace: core.space.DerivedTx,
space: core.space.DerivedTx
}
}
export function loadBrandingMap (brandingPath?: string): BrandingMap {
let brandings: BrandingMap = {}
if (brandingPath !== undefined && brandingPath !== '') {
brandings = JSON.parse(fs.readFileSync(brandingPath, 'utf8'))
for (const [host, value] of Object.entries(brandings)) {
const protocol = value.protocol ?? 'https'
value.front = `${protocol}://${host}/`
}
}
return brandings
}