mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-27 20:14:57 +02:00
Initial rest RPC (#8076)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
+38
-16
@@ -24,6 +24,8 @@ import {
|
||||
type FindOptions,
|
||||
type FindResult,
|
||||
type MeasureContext,
|
||||
type PersonId,
|
||||
type PersonUuid,
|
||||
type Ref,
|
||||
type SearchOptions,
|
||||
type SearchQuery,
|
||||
@@ -31,9 +33,8 @@ import {
|
||||
type Timestamp,
|
||||
type Tx,
|
||||
type TxCUD,
|
||||
type PersonId,
|
||||
type WorkspaceDataId,
|
||||
type PersonUuid
|
||||
type TxResult,
|
||||
type WorkspaceDataId
|
||||
} from '@hcengineering/core'
|
||||
import { PlatformError, unknownError } from '@hcengineering/platform'
|
||||
import {
|
||||
@@ -164,7 +165,14 @@ export class ClientSession implements Session {
|
||||
await ctx.sendResponse(ctx.requestId, await ctx.pipeline.searchFulltext(ctx.ctx, query, options))
|
||||
}
|
||||
|
||||
async tx (ctx: ClientSessionCtx, tx: Tx): Promise<void> {
|
||||
async txRaw (
|
||||
ctx: ClientSessionCtx,
|
||||
tx: Tx
|
||||
): Promise<{
|
||||
result: TxResult
|
||||
broadcastPromise: Promise<void>
|
||||
asyncsPromise: Promise<void> | undefined
|
||||
}> {
|
||||
this.lastRequest = Date.now()
|
||||
this.total.tx++
|
||||
this.current.tx++
|
||||
@@ -173,31 +181,45 @@ export class ClientSession implements Session {
|
||||
let cid = 'client_' + generateId()
|
||||
ctx.ctx.id = cid
|
||||
let onEnd = useReserveContext ? ctx.pipeline.context.adapterManager?.reserveContext?.(cid) : undefined
|
||||
let result: TxResult
|
||||
try {
|
||||
const result = await ctx.pipeline.tx(ctx.ctx, [tx])
|
||||
|
||||
// Send result immideately
|
||||
await ctx.sendResponse(ctx.requestId, result)
|
||||
|
||||
// We need to broadcast all collected transactions
|
||||
await ctx.pipeline.handleBroadcast(ctx.ctx)
|
||||
result = await ctx.pipeline.tx(ctx.ctx, [tx])
|
||||
} finally {
|
||||
onEnd?.()
|
||||
}
|
||||
// Send result immideately
|
||||
await ctx.sendResponse(ctx.requestId, result)
|
||||
|
||||
// We need to broadcast all collected transactions
|
||||
const broadcastPromise = ctx.pipeline.handleBroadcast(ctx.ctx)
|
||||
|
||||
// ok we could perform async requests if any
|
||||
const asyncs = (ctx.ctx.contextData as SessionData).asyncRequests ?? []
|
||||
let asyncsPromise: Promise<void> | undefined
|
||||
if (asyncs.length > 0) {
|
||||
cid = 'client_async_' + generateId()
|
||||
ctx.ctx.id = cid
|
||||
onEnd = useReserveContext ? ctx.pipeline.context.adapterManager?.reserveContext?.(cid) : undefined
|
||||
try {
|
||||
for (const r of (ctx.ctx.contextData as SessionData).asyncRequests ?? []) {
|
||||
await r()
|
||||
const handleAyncs = async (): Promise<void> => {
|
||||
try {
|
||||
for (const r of (ctx.ctx.contextData as SessionData).asyncRequests ?? []) {
|
||||
await r()
|
||||
}
|
||||
} finally {
|
||||
onEnd?.()
|
||||
}
|
||||
} finally {
|
||||
onEnd?.()
|
||||
}
|
||||
asyncsPromise = handleAyncs()
|
||||
}
|
||||
|
||||
return { result, broadcastPromise, asyncsPromise }
|
||||
}
|
||||
|
||||
async tx (ctx: ClientSessionCtx, tx: Tx): Promise<void> {
|
||||
const { broadcastPromise, asyncsPromise } = await this.txRaw(ctx, tx)
|
||||
await broadcastPromise
|
||||
if (asyncsPromise !== undefined) {
|
||||
await asyncsPromise
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ export interface Timeouts {
|
||||
reconnectTimeout: number // Default 3 seconds
|
||||
}
|
||||
|
||||
class TSessionManager implements SessionManager {
|
||||
export class TSessionManager implements SessionManager {
|
||||
private readonly statusPromises = new Map<string, Promise<void>>()
|
||||
readonly workspaces = new Map<WorkspaceUuid, Workspace>()
|
||||
checkInterval: any
|
||||
@@ -981,7 +981,7 @@ class TSessionManager implements SessionManager {
|
||||
createOpContext (
|
||||
ctx: MeasureContext,
|
||||
pipeline: Pipeline,
|
||||
request: Request<any>,
|
||||
requestId: Request<any>['id'],
|
||||
service: Session,
|
||||
ws: ConnectionSocket,
|
||||
workspace: WorkspaceUuid
|
||||
@@ -990,7 +990,7 @@ class TSessionManager implements SessionManager {
|
||||
return {
|
||||
ctx,
|
||||
pipeline,
|
||||
requestId: request.id,
|
||||
requestId,
|
||||
sendResponse: (reqId, msg) =>
|
||||
sendResponse(ctx, service, ws, {
|
||||
id: reqId,
|
||||
@@ -1072,6 +1072,7 @@ class TSessionManager implements SessionManager {
|
||||
return
|
||||
}
|
||||
if (request.id === -2 && request.method === 'forceClose') {
|
||||
// TODO: we chould allow this only for admin or system accounts
|
||||
let done = false
|
||||
const wsRef = this.workspaces.get(workspace)
|
||||
if (wsRef?.upgrade ?? false) {
|
||||
@@ -1106,7 +1107,7 @@ class TSessionManager implements SessionManager {
|
||||
const params = [...request.params]
|
||||
|
||||
await ctx.with('🧨 process', {}, (callTx) =>
|
||||
f.apply(service, [this.createOpContext(callTx, pipeline, request, service, ws, workspace), ...params])
|
||||
f.apply(service, [this.createOpContext(callTx, pipeline, request.id, service, ws), ...params])
|
||||
)
|
||||
} catch (err: any) {
|
||||
Analytics.handleError(err)
|
||||
@@ -1131,6 +1132,59 @@ class TSessionManager implements SessionManager {
|
||||
})
|
||||
}
|
||||
|
||||
handleRPC<S extends Session>(
|
||||
requestCtx: MeasureContext,
|
||||
service: S,
|
||||
ws: ConnectionSocket,
|
||||
operation: (ctx: ClientSessionCtx) => Promise<void>
|
||||
): Promise<void> {
|
||||
const userCtx = requestCtx.newChild('📞 client', {})
|
||||
|
||||
// Calculate total number of clients
|
||||
const reqId = generateId()
|
||||
|
||||
const st = Date.now()
|
||||
return userCtx
|
||||
.with('🧭 handleRPC', {}, async (ctx) => {
|
||||
if (service.workspace.closing !== undefined) {
|
||||
throw new Error('Workspace is closing')
|
||||
}
|
||||
|
||||
service.requests.set(reqId, {
|
||||
id: reqId,
|
||||
params: {},
|
||||
start: st
|
||||
})
|
||||
|
||||
const pipeline =
|
||||
service.workspace.pipeline instanceof Promise ? await service.workspace.pipeline : service.workspace.pipeline
|
||||
|
||||
try {
|
||||
const uctx = this.createOpContext(ctx, pipeline, reqId, service, ws)
|
||||
await operation(uctx)
|
||||
} catch (err: any) {
|
||||
Analytics.handleError(err)
|
||||
if (LOGGING_ENABLED) {
|
||||
this.ctx.error('error handle request', { error: err })
|
||||
}
|
||||
ws.send(
|
||||
ctx,
|
||||
{
|
||||
id: reqId,
|
||||
error: unknownError(err),
|
||||
result: JSON.parse(JSON.stringify(err?.stack))
|
||||
},
|
||||
service.binaryMode,
|
||||
service.useCompression
|
||||
)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
userCtx.end()
|
||||
service.requests.delete(reqId)
|
||||
})
|
||||
}
|
||||
|
||||
private async handleHello<S extends Session>(
|
||||
request: Request<any>,
|
||||
service: S,
|
||||
|
||||
Reference in New Issue
Block a user