mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
UBERF-7081 Fix empty collaborative content comparison (#5693)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
parent
ea388c0933
commit
9903d9e7a7
@ -116,33 +116,53 @@ describe('areEqualMarkups', () => {
|
||||
expect(areEqualMarkups(markup, markup)).toBeTruthy()
|
||||
})
|
||||
it('returns true for empty content', async () => {
|
||||
const markup1 = '{"type":"doc","content":[{"type":"paragraph"}]}'
|
||||
const markup2 = '{"type":"doc","content":[{"type":"paragraph","content":[]}]}'
|
||||
expect(areEqualMarkups(markup1, markup2)).toBeTruthy()
|
||||
expect(
|
||||
areEqualMarkups('{"type":"doc","content":[]}', '{"type":"doc","content":[{"type":"paragraph"}]}')
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
areEqualMarkups(
|
||||
'{"type":"doc","content":[{"type":"paragraph"}]}',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[]}]}'
|
||||
)
|
||||
).toBeTruthy()
|
||||
})
|
||||
it('returns true for similar content', async () => {
|
||||
const markup1 = '{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello"}]}]}'
|
||||
const markup2 =
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello","content":[],"marks":[],"attrs": {"color": null}}]}]}'
|
||||
expect(areEqualMarkups(markup1, markup2)).toBeTruthy()
|
||||
it('returns true for same content but empty marks and attrs', async () => {
|
||||
expect(
|
||||
areEqualMarkups(
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello"}]}]}',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello","content":[],"marks":[],"attrs": {"color": null}}]}]}'
|
||||
)
|
||||
).toBeTruthy()
|
||||
})
|
||||
it('returns false for the same content with different spaces', async () => {
|
||||
const markup1 = '{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello"}]}]}'
|
||||
const markup2 =
|
||||
'{"type":"doc","content":[{"type":"hardBreak"},{"type":"paragraph","content":[{"type":"text","text":"hello"}]},{"type":"hardBreak"}]}'
|
||||
expect(areEqualMarkups(markup1, markup2)).toBeFalsy()
|
||||
it('returns false for same content but trailing hard breaks', async () => {
|
||||
expect(
|
||||
areEqualMarkups(
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello","marks":[]}]}]}',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello"},{"type":"hardBreak"}]}]}'
|
||||
)
|
||||
).toBeFalsy()
|
||||
expect(
|
||||
areEqualMarkups(
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello"}]}]}',
|
||||
'{"type":"doc","content":[{"type":"hardBreak"},{"type":"paragraph","content":[{"type":"text","text":"hello"}]},{"type":"hardBreak"}]}'
|
||||
)
|
||||
).toBeFalsy()
|
||||
})
|
||||
it('returns false for different content', async () => {
|
||||
const markup1 = '{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello"}]}]}'
|
||||
const markup2 = '{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"world"}]}]}'
|
||||
expect(areEqualMarkups(markup1, markup2)).toBeFalsy()
|
||||
expect(
|
||||
areEqualMarkups(
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello"}]}]}',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"world"}]}]}'
|
||||
)
|
||||
).toBeFalsy()
|
||||
})
|
||||
it('returns false for different marks', async () => {
|
||||
const markup1 =
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello","marks":[{"type":"bold"}]}]}]}'
|
||||
const markup2 =
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello","marks":[{"type":"italic"}]}]}]}'
|
||||
expect(areEqualMarkups(markup1, markup2)).toBeFalsy()
|
||||
expect(
|
||||
areEqualMarkups(
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello","marks":[{"type":"bold"}]}]}]}',
|
||||
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hello","marks":[{"type":"italic"}]}]}]}'
|
||||
)
|
||||
).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -45,7 +45,14 @@ export function areEqualMarkups (markup1: Markup, markup2: Markup): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
return equalNodes(markupToJSON(markup1), markupToJSON(markup2))
|
||||
const node1 = markupToJSON(markup1)
|
||||
const node2 = markupToJSON(markup2)
|
||||
|
||||
if (isEmptyNode(node1) && isEmptyNode(node2)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return equalNodes(node1, node2)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
@ -16,6 +16,7 @@
|
||||
import { DocumentId, parseDocumentId } from '@hcengineering/collaborator-client'
|
||||
import { isReadonlyDoc } from '@hcengineering/collaboration'
|
||||
import { MeasureContext } from '@hcengineering/core'
|
||||
import { decodeToken } from '@hcengineering/server-token'
|
||||
import { Extension, onAuthenticatePayload } from '@hocuspocus/server'
|
||||
|
||||
import { getWorkspaceInfo } from '../account'
|
||||
@ -34,14 +35,18 @@ export class AuthenticationExtension implements Extension {
|
||||
|
||||
async onAuthenticate (data: onAuthenticatePayload): Promise<Context> {
|
||||
const ctx = this.configuration.ctx
|
||||
const { workspaceUrl, collaborativeDoc } = parseDocumentId(data.documentName as DocumentId)
|
||||
const { workspaceUrl: workspace, collaborativeDoc } = parseDocumentId(data.documentName as DocumentId)
|
||||
|
||||
return await ctx.with('authenticate', { workspace }, async () => {
|
||||
const token = decodeToken(data.token)
|
||||
|
||||
ctx.info('authenticate', { workspace, mode: token.extra?.mode ?? '' })
|
||||
|
||||
return await ctx.with('authenticate', { workspace: workspaceUrl }, async () => {
|
||||
// verify workspace can be accessed with the token
|
||||
const workspaceInfo = await getWorkspaceInfo(data.token)
|
||||
|
||||
// verify workspace url in the document matches the token
|
||||
if (workspaceInfo.workspace !== workspaceUrl) {
|
||||
if (workspaceInfo.workspace !== workspace) {
|
||||
throw new Error('documentName must include workspace')
|
||||
}
|
||||
|
||||
|
@ -49,18 +49,21 @@ export class StorageExtension implements Extension {
|
||||
}
|
||||
|
||||
async onLoadDocument ({ context, documentName }: withContext<onLoadDocumentPayload>): Promise<any> {
|
||||
this.configuration.ctx.info('load document', { documentName })
|
||||
const { connectionId } = context
|
||||
|
||||
this.configuration.ctx.info('load document', { documentName, connectionId })
|
||||
return await this.loadDocument(documentName as DocumentId, context)
|
||||
}
|
||||
|
||||
async onStoreDocument ({ context, documentName, document }: withContext<onStoreDocumentPayload>): Promise<void> {
|
||||
const { ctx } = this.configuration
|
||||
const { connectionId } = context
|
||||
|
||||
ctx.info('store document', { documentName })
|
||||
ctx.info('store document', { documentName, connectionId })
|
||||
|
||||
const collaborators = this.collaborators.get(documentName)
|
||||
if (collaborators === undefined || collaborators.size === 0) {
|
||||
ctx.info('no changes for document', { documentName })
|
||||
ctx.info('no changes for document', { documentName, connectionId })
|
||||
return
|
||||
}
|
||||
|
||||
@ -83,7 +86,7 @@ export class StorageExtension implements Extension {
|
||||
|
||||
const collaborators = this.collaborators.get(documentName)
|
||||
if (collaborators === undefined || !collaborators.has(connectionId)) {
|
||||
ctx.info('no changes for document', { documentName })
|
||||
ctx.info('no changes for document', { documentName, connectionId })
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -178,9 +178,6 @@ export async function start (
|
||||
return
|
||||
}
|
||||
|
||||
const token = decodeToken(authHeader.split(' ')[1])
|
||||
const context = getContext(token)
|
||||
|
||||
const request = req.body as RpcRequest
|
||||
const method = methods[request.method]
|
||||
if (method === undefined) {
|
||||
@ -189,6 +186,10 @@ export async function start (
|
||||
}
|
||||
res.status(400).send(response)
|
||||
} else {
|
||||
const token = decodeToken(authHeader.split(' ')[1])
|
||||
const context = getContext(token)
|
||||
|
||||
rpcCtx.info('rpc', { method: request.method, connectionId: context.connectionId, mode: token.extra?.mode ?? '' })
|
||||
await rpcCtx.with('/rpc', { method: request.method }, async (ctx) => {
|
||||
try {
|
||||
const response: RpcResponse = await rpcCtx.with(request.method, {}, async (ctx) => {
|
||||
|
Loading…
Reference in New Issue
Block a user