diff --git a/tdrive/backend/node/src/services/documents/services/index.ts b/tdrive/backend/node/src/services/documents/services/index.ts index 0ba2e802..5e82cb85 100644 --- a/tdrive/backend/node/src/services/documents/services/index.ts +++ b/tdrive/backend/node/src/services/documents/services/index.ts @@ -977,6 +977,8 @@ export class DocumentsService { return null; } + //TODO: This needs to try in a loop depending on oo-connector response + // when there is already a key let newKey: string; try { newKey = EditingSessionKeyFormat.generate( @@ -986,6 +988,7 @@ export class DocumentsService { context.user.id, ); } catch (e) { + logger.error(`Error generating new editing_session_key: ${e}`, { error: e }); CrudException.throwMe(e, new CrudException("Error generating new editing_session_key", 500)); } @@ -1026,12 +1029,15 @@ export class DocumentsService { * @param editing_session_key Editing key of the DriveFile * @param file Multipart files from the incoming http request * @param options Optional upload information from the request + * @param keepEditing If `true`, the file will be saved as a new version, + * and the DriveFile will keep its editing_session_key. If `true`, a file is required. * @param context */ - endEditing = async ( + updateEditing = async ( editing_session_key: string, file: MultipartFile, options: UploadOptions, + keepEditing: boolean, context: CompanyExecutionContext, ) => { if (!context) { @@ -1043,6 +1049,7 @@ export class DocumentsService { throw new CrudException("Invalid editing_session_key", 400); } + //TODO If the app is the "user" calling, set user to that from the parsed key try { const parsedKey = EditingSessionKeyFormat.parse(editing_session_key); context = { @@ -1087,26 +1094,31 @@ export class DocumentsService { }, context, ); + } else if (keepEditing) { + this.logger.error("Inconsistent endEditing call"); + throw new CrudException("Inconsistent endEditing call", 500); } - try { - const result = await this.repository.atomicCompareAndSet( - driveFile, - "editing_session_key", - editing_session_key, - null, - ); - if (!result.didSet) - throw new Error( - `Couldn't set editing_session_key ${JSON.stringify( - editing_session_key, - )} on DriveFile ${JSON.stringify(driveFile.id)} because it is ${JSON.stringify( - result.currentValue, - )}`, + if (!keepEditing) { + try { + const result = await this.repository.atomicCompareAndSet( + driveFile, + "editing_session_key", + editing_session_key, + null, ); - } catch (error) { - logger.error({ error: `${error}` }, "Failed to cancel editing Drive item"); - CrudException.throwMe(error, new CrudException("Failed to cancel editing Drive item", 500)); + if (!result.didSet) + throw new Error( + `Couldn't set editing_session_key ${JSON.stringify( + editing_session_key, + )} on DriveFile ${JSON.stringify(driveFile.id)} because it is ${JSON.stringify( + result.currentValue, + )}`, + ); + } catch (error) { + logger.error({ error: `${error}` }, "Failed to cancel editing Drive item"); + CrudException.throwMe(error, new CrudException("Failed to cancel editing Drive item", 500)); + } } }; diff --git a/tdrive/backend/node/src/services/documents/web/controllers/documents.ts b/tdrive/backend/node/src/services/documents/web/controllers/documents.ts index c654a21c..a094f604 100644 --- a/tdrive/backend/node/src/services/documents/web/controllers/documents.ts +++ b/tdrive/backend/node/src/services/documents/web/controllers/documents.ts @@ -381,10 +381,11 @@ export class DocumentsController { if (!editing_session_key) throw new CrudException("Missing editing_session_key", 400); - return await globalResolver.services.documents.documents.endEditing( + return await globalResolver.services.documents.documents.updateEditing( editing_session_key, null, null, + false, context, ); } catch (error) { @@ -394,12 +395,13 @@ export class DocumentsController { }; //TODO: will need a save under session key, but without ending the edit (for force saves) /** - * Finish an editing session for a given `editing_session_key` by uploading the new version of the File + * Finish an editing session for a given `editing_session_key` by uploading the new version of the File. + * Unless the `keepEditing` query param is `true`, then just save and stay in editing mode. */ - endEditing = async ( + updateEditing = async ( request: FastifyRequest<{ Params: ItemRequestByEditingSessionKeyParams; - Querystring: Record; + Querystring: { keepEditing?: string }; Body: { item: Partial; version: Partial; @@ -423,17 +425,19 @@ export class DocumentsController { waitForThumbnail: !!q.thumbnail_sync, ignoreThumbnails: false, }; - return await globalResolver.services.documents.documents.endEditing( + return await globalResolver.services.documents.documents.updateEditing( editing_session_key, file, options, + request.query.keepEditing == "true", context, ); } else { - return await globalResolver.services.documents.documents.endEditing( + return await globalResolver.services.documents.documents.updateEditing( editing_session_key, null, null, + true, context, ); } diff --git a/tdrive/backend/node/src/services/documents/web/routes.ts b/tdrive/backend/node/src/services/documents/web/routes.ts index 9da696fd..25fde912 100644 --- a/tdrive/backend/node/src/services/documents/web/routes.ts +++ b/tdrive/backend/node/src/services/documents/web/routes.ts @@ -99,7 +99,7 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, _options, next) method: "POST", url: editingSessionBase, preValidation: [fastify.authenticateOptional], - handler: documentsController.endEditing.bind(documentsController), + handler: documentsController.updateEditing.bind(documentsController), }); fastify.route({ diff --git a/tdrive/connectors/onlyoffice-connector/src/interfaces/drive.interface.ts b/tdrive/connectors/onlyoffice-connector/src/interfaces/drive.interface.ts index af8da61d..d3195931 100644 --- a/tdrive/connectors/onlyoffice-connector/src/interfaces/drive.interface.ts +++ b/tdrive/connectors/onlyoffice-connector/src/interfaces/drive.interface.ts @@ -21,6 +21,7 @@ export interface IDriveService { get: (params: DriveRequestParams) => Promise; createVersion: (params: { company_id: string; drive_file_id: string; file_id: string }) => Promise; beginEditingSession: (company_id: string, drive_file_id: string) => Promise; + addEditingSessionVersion: (editing_session_key: string, url: string) => Promise; endEditing: (editing_session_key: string, url: string) => Promise; getByEditingSessionKey: (params: { company_id: string; editing_session_key: string; user_token?: string }) => Promise; } diff --git a/tdrive/connectors/onlyoffice-connector/src/services/drive.service.ts b/tdrive/connectors/onlyoffice-connector/src/services/drive.service.ts index cf464406..6ba08b76 100644 --- a/tdrive/connectors/onlyoffice-connector/src/services/drive.service.ts +++ b/tdrive/connectors/onlyoffice-connector/src/services/drive.service.ts @@ -54,6 +54,7 @@ class DriveService implements IDriveService { try { const resource = await apiService.post<{}, { editingSessionKey: string }>({ url: `/internal/services/documents/v1/companies/${company_id}/item/${drive_file_id}/editing_session`, + token: user_token, payload: { editorApplicationId: 'tdrive_onlyoffice', appInstanceId: INSTANCE_ID ?? '', @@ -82,7 +83,15 @@ class DriveService implements IDriveService { } } - public async endEditing(editing_session_key: string, url: string) { + public async addEditingSessionVersion(editing_session_key: string, url: string, user_token?: string) { + return this.updateEditing(editing_session_key, url, true, user_token); + } + + public async endEditing(editing_session_key: string, url: string, user_token?: string) { + return this.updateEditing(editing_session_key, url, false, user_token); + } + + private async updateEditing(editing_session_key: string, url: string, keepEditing: boolean, user_token?: string) { try { if (!url) { throw Error('no url found'); @@ -109,9 +118,12 @@ class DriveService implements IDriveService { logger.info('Saving file version to Twake Drive: ', filename); + const queryString = keepEditing ? '?keepEditing=true' : ''; + await apiService.post({ - url: `/internal/services/documents/v1/editing_session/${encodeURIComponent(editing_session_key)}`, + url: `/internal/services/documents/v1/editing_session/${encodeURIComponent(editing_session_key)}` + queryString, payload: form, + token: user_token, headers: form.getHeaders(), }); } catch (error) {