UBERF-4725 Migrate collaborative content (#5717)

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov
2024-08-22 16:03:42 +05:00
committed by GitHub
parent 0e72b85978
commit df2a9b2708
97 changed files with 702 additions and 820 deletions
@@ -13,9 +13,10 @@
// limitations under the License.
//
import { yDocCopyXmlField } from '@hcengineering/collaboration'
import { type CopyContentRequest, type CopyContentResponse } from '@hcengineering/collaborator-client'
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'
@@ -25,17 +26,36 @@ export async function copyContent (
payload: CopyContentRequest,
params: RpcMethodParams
): Promise<CopyContentResponse> {
const { documentId, sourceField, targetField } = payload
const { hocuspocus } = params
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 connection.transact((document) => {
yDocCopyXmlField(document, sourceField, targetField)
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()
}
@@ -24,7 +24,7 @@ export async function getContent (
payload: GetContentRequest,
params: RpcMethodParams
): Promise<GetContentResponse> {
const { documentId, field } = payload
const { documentId } = payload
const { hocuspocus, transformer } = params
const connection = await ctx.with('connect', {}, async () => {
@@ -32,15 +32,17 @@ export async function getContent (
})
try {
const html = await ctx.with('transform', {}, async () => {
let content = ''
const content = await ctx.with('transform', {}, async () => {
const object: Record<string, string> = {}
await connection.transact((document) => {
content = transformer.fromYdoc(document, field)
document.share.forEach((_, field) => {
object[field] = transformer.fromYdoc(document, field)
})
})
return content
return object
})
return { html }
return { content }
} finally {
await connection.disconnect()
}
@@ -13,19 +13,13 @@
// limitations under the License.
//
import {
YDocVersion,
collaborativeHistoryDocId,
createYdocSnapshot,
yDocFromStorage,
yDocToStorage
} from '@hcengineering/collaboration'
import { YDocVersion, takeCollaborativeDocSnapshot } from '@hcengineering/collaboration'
import {
parseDocumentId,
type TakeSnapshotRequest,
type TakeSnapshotResponse
} from '@hcengineering/collaborator-client'
import { CollaborativeDocVersionHead, MeasureContext, collaborativeDocParse, generateId } from '@hcengineering/core'
import { CollaborativeDocVersionHead, MeasureContext, collaborativeDocParse } from '@hcengineering/core'
import { Doc as YDoc } from 'yjs'
import { Context } from '../../context'
import { RpcMethodParams } from '../rpc'
@@ -36,19 +30,19 @@ export async function takeSnapshot (
payload: TakeSnapshotRequest,
params: RpcMethodParams
): Promise<TakeSnapshotResponse> {
const { documentId, snapshotName, createdBy } = payload
const { documentId, snapshot } = payload
const { hocuspocus, storageAdapter } = params
const { workspaceId } = context
const version: YDocVersion = {
versionId: generateId(),
name: snapshotName,
createdBy,
versionId: snapshot.versionId,
name: snapshot.versionName ?? snapshot.versionId,
createdBy: snapshot.createdBy,
createdOn: Date.now()
}
const { collaborativeDoc } = parseDocumentId(documentId)
const { documentId: contentDocumentId, versionId } = collaborativeDocParse(collaborativeDoc)
const { versionId } = collaborativeDocParse(collaborativeDoc)
if (versionId !== CollaborativeDocVersionHead) {
throw new Error('invalid document version')
}
@@ -58,21 +52,10 @@ export async function takeSnapshot (
})
try {
// load history document directly from storage
const historyDocumentId = collaborativeHistoryDocId(contentDocumentId)
const yHistory =
(await ctx.with('yDocFromStorage', {}, async () => {
return await yDocFromStorage(ctx, storageAdapter, workspaceId, historyDocumentId)
})) ?? new YDoc()
const ydoc = connection.document ?? new YDoc()
await ctx.with('createYdocSnapshot', {}, async () => {
await connection.transact((yContent) => {
createYdocSnapshot(yContent, yHistory, version)
})
})
await ctx.with('yDocToStorage', {}, async () => {
await yDocToStorage(ctx, storageAdapter, workspaceId, historyDocumentId, yHistory)
await ctx.with('snapshot', {}, async () => {
await takeCollaborativeDocSnapshot(storageAdapter, workspaceId, collaborativeDoc, ydoc, version, ctx)
})
return { ...version }
@@ -14,8 +14,13 @@
//
import { MeasureContext } from '@hcengineering/core'
import { type UpdateContentRequest, type UpdateContentResponse } from '@hcengineering/collaborator-client'
import { applyUpdate, encodeStateAsUpdate } from 'yjs'
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 { Context } from '../../context'
import { RpcMethodParams } from '../rpc'
@@ -25,12 +30,18 @@ export async function updateContent (
payload: UpdateContentRequest,
params: RpcMethodParams
): Promise<UpdateContentResponse> {
const { documentId, field, html } = payload
const { hocuspocus, transformer } = params
const { documentId, content, snapshot } = payload
const { hocuspocus, transformer, storageAdapter } = params
const { workspaceId } = context
const update = await ctx.with('transform', {}, () => {
const ydoc = transformer.toYdoc(html, field)
return encodeStateAsUpdate(ydoc)
const updates = await ctx.with('transform', {}, () => {
const updates: Record<string, Uint8Array> = {}
Object.entries(content).forEach(([field, markup]) => {
const ydoc = transformer.toYdoc(markup, field)
updates[field] = encodeStateAsUpdate(ydoc)
})
return updates
})
const connection = await ctx.with('connect', {}, async () => {
@@ -40,13 +51,31 @@ export async function updateContent (
try {
await ctx.with('update', {}, async () => {
await connection.transact((document) => {
const fragment = document.getXmlFragment(field)
document.transact(() => {
fragment.delete(0, fragment.length)
applyUpdate(document, update)
Object.entries(updates).forEach(([field, update]) => {
const fragment = document.getXmlFragment(field)
fragment.delete(0, fragment.length)
applyUpdate(document, update)
})
})
})
})
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()
}