mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-21 15:59:15 +00:00
UBERF-4905 Change collaborator client payload (#4392)
Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
parent
b4d898be5b
commit
c0c17993cf
@ -13,7 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Class, Doc, Markup, Ref, concatLink } from '@hcengineering/core'
|
import { Class, Doc, Hierarchy, Markup, Ref, concatLink } from '@hcengineering/core'
|
||||||
|
import { minioDocumentId, mongodbDocumentId } from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -26,18 +27,32 @@ export interface CollaboratorClient {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function getClient (token: string, collaboratorUrl: string): CollaboratorClient {
|
export function getClient (hierarchy: Hierarchy, token: string, collaboratorUrl: string): CollaboratorClient {
|
||||||
return new CollaboratorClientImpl(token, collaboratorUrl)
|
return new CollaboratorClientImpl(hierarchy, token, collaboratorUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
class CollaboratorClientImpl implements CollaboratorClient {
|
class CollaboratorClientImpl implements CollaboratorClient {
|
||||||
constructor (
|
constructor (
|
||||||
|
private readonly hierarchy: Hierarchy,
|
||||||
private readonly token: string,
|
private readonly token: string,
|
||||||
private readonly collaboratorUrl: string
|
private readonly collaboratorUrl: string
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
initialContentId (classId: Ref<Class<Doc>>, docId: Ref<Doc>, attribute: string): string {
|
||||||
|
const domain = this.hierarchy.getDomain(classId)
|
||||||
|
return mongodbDocumentId(domain, docId, attribute)
|
||||||
|
}
|
||||||
|
|
||||||
async get (classId: Ref<Class<Doc>>, docId: Ref<Doc>, attribute: string): Promise<Markup> {
|
async get (classId: Ref<Class<Doc>>, docId: Ref<Doc>, attribute: string): Promise<Markup> {
|
||||||
const url = concatLink(this.collaboratorUrl, `/api/content/${classId}/${docId}/${attribute}`)
|
const documentId = encodeURIComponent(minioDocumentId(docId, attribute))
|
||||||
|
const initialContentId = encodeURIComponent(this.initialContentId(classId, docId, attribute))
|
||||||
|
attribute = encodeURIComponent(attribute)
|
||||||
|
|
||||||
|
const url = concatLink(
|
||||||
|
this.collaboratorUrl,
|
||||||
|
`/api/content/${documentId}/${attribute}?initialContentId=${initialContentId}`
|
||||||
|
)
|
||||||
|
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
@ -50,7 +65,15 @@ class CollaboratorClientImpl implements CollaboratorClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update (classId: Ref<Class<Doc>>, docId: Ref<Doc>, attribute: string, value: Markup): Promise<void> {
|
async update (classId: Ref<Class<Doc>>, docId: Ref<Doc>, attribute: string, value: Markup): Promise<void> {
|
||||||
const url = concatLink(this.collaboratorUrl, `/api/content/${classId}/${docId}/${attribute}`)
|
const documentId = encodeURIComponent(minioDocumentId(docId, attribute))
|
||||||
|
const initialContentId = encodeURIComponent(this.initialContentId(classId, docId, attribute))
|
||||||
|
attribute = encodeURIComponent(attribute)
|
||||||
|
|
||||||
|
const url = concatLink(
|
||||||
|
this.collaboratorUrl,
|
||||||
|
`/api/content/${documentId}/${attribute}?initialContentId=${initialContentId}`
|
||||||
|
)
|
||||||
|
|
||||||
await fetch(url, {
|
await fetch(url, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -14,3 +14,4 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
export { type CollaboratorClient, getClient } from './client'
|
export { type CollaboratorClient, getClient } from './client'
|
||||||
|
export * from './utils'
|
||||||
|
24
packages/collaborator-client/src/utils.ts
Normal file
24
packages/collaborator-client/src/utils.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// 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 { Doc, Domain, Ref } from '@hcengineering/core'
|
||||||
|
|
||||||
|
export function minioDocumentId (docId: Ref<Doc>, attribute?: string): string {
|
||||||
|
return attribute !== undefined ? `minio://${docId}%${attribute}` : `minio://${docId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mongodbDocumentId (domain: Domain, docId: Ref<Doc>, attribute: string): string {
|
||||||
|
return `mongodb://${domain}/${docId}/${attribute}`
|
||||||
|
}
|
@ -13,20 +13,22 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { type CollaboratorClient, getClient } from '@hcengineering/collaborator-client'
|
import { type CollaboratorClient, getClient as getCollaborator } from '@hcengineering/collaborator-client'
|
||||||
import type { Class, Doc, Markup, Ref } from '@hcengineering/core'
|
import type { Class, Doc, Markup, Ref } from '@hcengineering/core'
|
||||||
import { getMetadata } from '@hcengineering/platform'
|
import { getMetadata } from '@hcengineering/platform'
|
||||||
|
|
||||||
|
import { getClient } from '.'
|
||||||
import presentation from './plugin'
|
import presentation from './plugin'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function getCollaboratorClient (): CollaboratorClient {
|
export function getCollaboratorClient (): CollaboratorClient {
|
||||||
|
const hierarchy = getClient().getHierarchy()
|
||||||
const token = getMetadata(presentation.metadata.Token) ?? ''
|
const token = getMetadata(presentation.metadata.Token) ?? ''
|
||||||
const collaboratorURL = getMetadata(presentation.metadata.CollaboratorUrl) ?? ''
|
const collaboratorURL = getMetadata(presentation.metadata.CollaboratorUrl) ?? ''
|
||||||
|
|
||||||
return getClient(token, collaboratorURL)
|
return getCollaborator(hierarchy, token, collaboratorURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -153,18 +153,18 @@ export async function start (
|
|||||||
|
|
||||||
const restCtx = ctx.newChild('REST', {})
|
const restCtx = ctx.newChild('REST', {})
|
||||||
|
|
||||||
const getContext = (token: Token, documentId: string): Context => {
|
const getContext = (token: Token, initialContentId?: string): Context => {
|
||||||
return {
|
return {
|
||||||
connectionId: generateId(),
|
connectionId: generateId(),
|
||||||
workspaceId: token.workspace,
|
workspaceId: token.workspace,
|
||||||
clientFactory: getClientFactory(token, controller),
|
clientFactory: getClientFactory(token, controller),
|
||||||
initialContentId: documentId,
|
initialContentId: initialContentId ?? '',
|
||||||
targetContentId: ''
|
targetContentId: ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
app.get('/api/content/:classId/:docId/:attribute', async (req, res) => {
|
app.get('/api/content/:documentId/:field', async (req, res) => {
|
||||||
console.log('handle request', req.method, req.url)
|
console.log('handle request', req.method, req.url)
|
||||||
|
|
||||||
const authHeader = req.headers.authorization
|
const authHeader = req.headers.authorization
|
||||||
@ -176,22 +176,21 @@ export async function start (
|
|||||||
const token = authHeader.split(' ')[1]
|
const token = authHeader.split(' ')[1]
|
||||||
const decodedToken = decodeToken(token)
|
const decodedToken = decodeToken(token)
|
||||||
|
|
||||||
const docId = req.params.docId
|
const documentId = req.params.documentId
|
||||||
const attribute = req.params.attribute
|
const field = req.params.field
|
||||||
|
const initialContentId = req.query.initialContentId as string
|
||||||
|
|
||||||
if (docId === undefined || docId === '') {
|
if (documentId === undefined || documentId === '') {
|
||||||
res.status(400).send({ err: "'docId' is missing" })
|
res.status(400).send({ err: "'documentId' is missing" })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attribute === undefined || attribute === '') {
|
if (field === undefined || field === '') {
|
||||||
res.status(400).send({ err: "'attribute' is missing" })
|
res.status(400).send({ err: "'field' is missing" })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const documentId = `minio://${docId}%${attribute}`
|
const context = getContext(decodedToken, initialContentId)
|
||||||
|
|
||||||
const context = getContext(decodedToken, documentId)
|
|
||||||
|
|
||||||
await restCtx.with(`${req.method} /content`, {}, async (ctx) => {
|
await restCtx.with(`${req.method} /content`, {}, async (ctx) => {
|
||||||
const connection = await ctx.with('connect', {}, async () => {
|
const connection = await ctx.with('connect', {}, async () => {
|
||||||
@ -202,7 +201,7 @@ export async function start (
|
|||||||
const html = await ctx.with('transform', {}, async () => {
|
const html = await ctx.with('transform', {}, async () => {
|
||||||
let content = ''
|
let content = ''
|
||||||
await connection.transact((document) => {
|
await connection.transact((document) => {
|
||||||
content = transformer.fromYdoc(document, attribute)
|
content = transformer.fromYdoc(document, field)
|
||||||
})
|
})
|
||||||
return content
|
return content
|
||||||
})
|
})
|
||||||
@ -221,7 +220,7 @@ export async function start (
|
|||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
app.put('/api/content/:classId/:docId/:attribute', async (req, res) => {
|
app.put('/api/content/:documentId/:field', async (req, res) => {
|
||||||
console.log('handle request', req.method, req.url)
|
console.log('handle request', req.method, req.url)
|
||||||
|
|
||||||
const authHeader = req.headers.authorization
|
const authHeader = req.headers.authorization
|
||||||
@ -233,27 +232,26 @@ export async function start (
|
|||||||
const token = authHeader.split(' ')[1]
|
const token = authHeader.split(' ')[1]
|
||||||
const decodedToken = decodeToken(token)
|
const decodedToken = decodeToken(token)
|
||||||
|
|
||||||
const docId = req.params.docId
|
const documentId = req.params.documentId
|
||||||
const attribute = req.params.attribute
|
const field = req.params.field
|
||||||
|
const initialContentId = req.query.initialContentId as string
|
||||||
if (docId === undefined || docId === '') {
|
|
||||||
res.status(400).send({ err: "'docId' is missing" })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attribute === undefined || attribute === '') {
|
|
||||||
res.status(400).send({ err: "'attribute' is missing" })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const documentId = `minio://${docId}%${attribute}`
|
|
||||||
const data = req.body.html ?? '<p></p>'
|
const data = req.body.html ?? '<p></p>'
|
||||||
|
|
||||||
const context = getContext(decodedToken, documentId)
|
if (documentId === undefined || documentId === '') {
|
||||||
|
res.status(400).send({ err: "'documentId' is missing" })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field === undefined || field === '') {
|
||||||
|
res.status(400).send({ err: "'field' is missing" })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = getContext(decodedToken, initialContentId)
|
||||||
|
|
||||||
await restCtx.with(`${req.method} /content`, {}, async (ctx) => {
|
await restCtx.with(`${req.method} /content`, {}, async (ctx) => {
|
||||||
const update = await ctx.with('transform', {}, () => {
|
const update = await ctx.with('transform', {}, () => {
|
||||||
const ydoc = transformer.toYdoc(data, attribute)
|
const ydoc = transformer.toYdoc(data, field)
|
||||||
return encodeStateAsUpdate(ydoc)
|
return encodeStateAsUpdate(ydoc)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -264,7 +262,7 @@ export async function start (
|
|||||||
try {
|
try {
|
||||||
await ctx.with('update', {}, async () => {
|
await ctx.with('update', {}, async () => {
|
||||||
await connection.transact((document) => {
|
await connection.transact((document) => {
|
||||||
const fragment = document.getXmlFragment(attribute)
|
const fragment = document.getXmlFragment(field)
|
||||||
document.transact((tr) => {
|
document.transact((tr) => {
|
||||||
fragment.delete(0, fragment.length)
|
fragment.delete(0, fragment.length)
|
||||||
applyUpdate(document, update)
|
applyUpdate(document, update)
|
||||||
|
Loading…
Reference in New Issue
Block a user