Cloud collaborator refactoring (#6424)

This commit is contained in:
Alexander Onnikov
2024-08-29 15:10:37 +07:00
committed by GitHub
parent 42b19c39c7
commit 4ff8a4559f
52 changed files with 762 additions and 998 deletions
@@ -1,57 +0,0 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { yDocBranchWithGC } from '@hcengineering/collaboration'
import { type BranchDocumentRequest, type BranchDocumentResponse } from '@hcengineering/collaborator-client'
import { MeasureContext } from '@hcengineering/core'
import { applyUpdate, encodeStateAsUpdate } from 'yjs'
import { Context } from '../../context'
import { RpcMethodParams } from '../rpc'
export async function branchDocument (
ctx: MeasureContext,
context: Context,
payload: BranchDocumentRequest,
params: RpcMethodParams
): Promise<BranchDocumentResponse> {
const { sourceDocumentId, targetDocumentId } = payload
const { hocuspocus } = params
const sourceConnection = await ctx.with('connect', { type: 'source' }, async () => {
return await hocuspocus.openDirectConnection(sourceDocumentId, context)
})
const targetConnection = await ctx.with('connect', { type: 'target' }, async () => {
return await hocuspocus.openDirectConnection(targetDocumentId, context)
})
try {
let update = new Uint8Array()
await sourceConnection.transact((document) => {
const copy = yDocBranchWithGC(document)
update = encodeStateAsUpdate(copy)
})
await targetConnection.transact((document) => {
applyUpdate(document, update)
})
} finally {
await sourceConnection.disconnect()
await targetConnection.disconnect()
}
return {}
}
@@ -1,64 +0,0 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { YDocVersion, takeCollaborativeDocSnapshot, yDocCopyXmlField } from '@hcengineering/collaboration'
import { parseDocumentId, type CopyContentRequest, type CopyContentResponse } from '@hcengineering/collaborator-client'
import { MeasureContext } from '@hcengineering/core'
import { Doc as YDoc } from 'yjs'
import { Context } from '../../context'
import { RpcMethodParams } from '../rpc'
export async function copyContent (
ctx: MeasureContext,
context: Context,
payload: CopyContentRequest,
params: RpcMethodParams
): Promise<CopyContentResponse> {
const { documentId, sourceField, targetField, snapshot } = payload
const { hocuspocus, storageAdapter } = params
const { workspaceId } = context
const connection = await ctx.with('connect', {}, async () => {
return await hocuspocus.openDirectConnection(documentId, context)
})
try {
await ctx.with('copy', {}, async () => {
await connection.transact((document) => {
yDocCopyXmlField(document, sourceField, targetField)
})
})
if (snapshot !== undefined && snapshot.versionId !== 'HEAD') {
const ydoc = connection.document ?? new YDoc()
const { collaborativeDoc } = parseDocumentId(documentId)
const version: YDocVersion = {
versionId: snapshot.versionId,
name: snapshot.versionName ?? snapshot.versionId,
createdBy: snapshot.createdBy,
createdOn: Date.now()
}
await ctx.with('snapshot', {}, async () => {
await takeCollaborativeDocSnapshot(storageAdapter, workspaceId, collaborativeDoc, ydoc, version, ctx)
})
}
} finally {
await connection.disconnect()
}
return {}
}
@@ -21,10 +21,10 @@ import { RpcMethodParams } from '../rpc'
export async function getContent (
ctx: MeasureContext,
context: Context,
documentId: string,
payload: GetContentRequest,
params: RpcMethodParams
): Promise<GetContentResponse> {
const { documentId } = payload
const { hocuspocus, transformer } = params
const connection = await ctx.with('connect', {}, async () => {
+1 -9
View File
@@ -14,18 +14,10 @@
//
import { getContent } from './getContent'
import { copyContent } from './copyContent'
import { updateContent } from './updateContent'
import { branchDocument } from './branchDocument'
import { removeDocument } from './removeDocument'
import { takeSnapshot } from './takeSnapshot'
import { RpcMethod } from '../rpc'
export const methods: Record<string, RpcMethod> = {
getContent,
copyContent,
updateContent,
branchDocument,
removeDocument,
takeSnapshot
updateContent
}
@@ -1,53 +0,0 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { collaborativeHistoryDocId } from '@hcengineering/collaboration'
import {
parseDocumentId,
type RemoveDocumentRequest,
type RemoveDocumentResponse
} from '@hcengineering/collaborator-client'
import { MeasureContext, collaborativeDocParse } from '@hcengineering/core'
import { Context } from '../../context'
import { RpcMethodParams } from '../rpc'
export async function removeDocument (
ctx: MeasureContext,
context: Context,
payload: RemoveDocumentRequest,
params: RpcMethodParams
): Promise<RemoveDocumentResponse> {
const { documentId } = payload
const { hocuspocus, storageAdapter } = params
const { workspaceId } = context
const document = hocuspocus.documents.get(documentId)
if (document !== undefined) {
hocuspocus.closeConnections(documentId)
hocuspocus.unloadDocument(document)
}
const { collaborativeDoc } = parseDocumentId(documentId)
const { documentId: contentDocumentId } = collaborativeDocParse(collaborativeDoc)
const historyDocumentId = collaborativeHistoryDocId(contentDocumentId)
try {
await storageAdapter.remove(ctx, workspaceId, [contentDocumentId, historyDocumentId])
} catch (err) {
ctx.error('failed to remove document', { documentId, error: err })
}
return {}
}
@@ -1,65 +0,0 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { YDocVersion, takeCollaborativeDocSnapshot } from '@hcengineering/collaboration'
import {
parseDocumentId,
type TakeSnapshotRequest,
type TakeSnapshotResponse
} from '@hcengineering/collaborator-client'
import { CollaborativeDocVersionHead, MeasureContext, collaborativeDocParse } from '@hcengineering/core'
import { Doc as YDoc } from 'yjs'
import { Context } from '../../context'
import { RpcMethodParams } from '../rpc'
export async function takeSnapshot (
ctx: MeasureContext,
context: Context,
payload: TakeSnapshotRequest,
params: RpcMethodParams
): Promise<TakeSnapshotResponse> {
const { documentId, snapshot } = payload
const { hocuspocus, storageAdapter } = params
const { workspaceId } = context
const version: YDocVersion = {
versionId: snapshot.versionId,
name: snapshot.versionName ?? snapshot.versionId,
createdBy: snapshot.createdBy,
createdOn: Date.now()
}
const { collaborativeDoc } = parseDocumentId(documentId)
const { versionId } = collaborativeDocParse(collaborativeDoc)
if (versionId !== CollaborativeDocVersionHead) {
throw new Error('invalid document version')
}
const connection = await ctx.with('connect', {}, async () => {
return await hocuspocus.openDirectConnection(documentId, context)
})
try {
const ydoc = connection.document ?? new YDoc()
await ctx.with('snapshot', {}, async () => {
await takeCollaborativeDocSnapshot(storageAdapter, workspaceId, collaborativeDoc, ydoc, version, ctx)
})
return { ...version }
} finally {
await connection.disconnect()
}
}
@@ -14,25 +14,20 @@
//
import { MeasureContext } from '@hcengineering/core'
import {
parseDocumentId,
type UpdateContentRequest,
type UpdateContentResponse
} from '@hcengineering/collaborator-client'
import { YDocVersion, takeCollaborativeDocSnapshot } from '@hcengineering/collaboration'
import { Doc as YDoc, applyUpdate, encodeStateAsUpdate } from 'yjs'
import { type UpdateContentRequest, type UpdateContentResponse } from '@hcengineering/collaborator-client'
import { applyUpdate, encodeStateAsUpdate } from 'yjs'
import { Context } from '../../context'
import { RpcMethodParams } from '../rpc'
export async function updateContent (
ctx: MeasureContext,
context: Context,
documentId: string,
payload: UpdateContentRequest,
params: RpcMethodParams
): Promise<UpdateContentResponse> {
const { documentId, content, snapshot } = payload
const { hocuspocus, transformer, storageAdapter } = params
const { workspaceId } = context
const { content } = payload
const { hocuspocus, transformer } = params
const updates = await ctx.with('transform', {}, () => {
const updates: Record<string, Uint8Array> = {}
@@ -41,6 +36,7 @@ export async function updateContent (
const ydoc = transformer.toYdoc(markup, field)
updates[field] = encodeStateAsUpdate(ydoc)
})
return updates
})
@@ -60,22 +56,6 @@ export async function updateContent (
})
})
})
if (snapshot !== undefined && snapshot.versionId !== 'HEAD') {
const ydoc = connection.document ?? new YDoc()
const { collaborativeDoc } = parseDocumentId(documentId)
const version: YDocVersion = {
versionId: snapshot.versionId,
name: snapshot.versionName ?? snapshot.versionId,
createdBy: snapshot.createdBy,
createdOn: Date.now()
}
await ctx.with('snapshot', {}, async () => {
await takeCollaborativeDocSnapshot(storageAdapter, workspaceId, collaborativeDoc, ydoc, version, ctx)
})
}
} finally {
await connection.disconnect()
}