🔀 Merge branch 'postgres-support-fix' into 525-515-548-523-onlyoffice-rework

This commit is contained in:
Eric Doughty-Papassideris
2024-09-14 22:56:31 +02:00
19 changed files with 450 additions and 168 deletions
@@ -0,0 +1,16 @@
import { CommandModule } from "yargs";
const command: CommandModule = {
describe: "Editing sessions tools",
command: "editing_session",
builder: yargs =>
yargs.commandDir("editing_session_cmds", {
visit: commandModule => commandModule.default,
}),
// eslint-disable-next-line @typescript-eslint/no-empty-function
handler: () => {
throw new Error("Missing sub-command");
},
};
export default command;
@@ -11,7 +11,7 @@ export interface DatabaseServiceAPI extends TdriveServiceProvider {
getConnector(): Connector;
/**
* Get entities manager (TODO: Find a better name...)
* Get entities manager
*/
getManager(): Manager<unknown>;
@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import _ from "lodash";
import { Connector } from "./connectors";
import { getEntityDefinition, unwrapPrimarykey } from "./utils";
@@ -9,13 +8,9 @@ import { DatabaseEntitiesRemovedEvent, DatabaseEntitiesSavedEvent } from "./type
import { localEventBus } from "../../../../framework/event-bus";
export default class EntityManager<EntityType extends Record<string, any>> {
private toInsert: EntityType[] = [];
private toUpdate: EntityType[] = [];
private toRemove: EntityType[] = [];
constructor(readonly connector: Connector) {}
public persist(entity: any): this {
public async persist(entity: any): Promise<this> {
logger.trace(
`services.database.orm.entity-manager.persist - entity: ${JSON.stringify(entity)}`,
);
@@ -62,18 +57,20 @@ export default class EntityManager<EntityType extends Record<string, any>> {
}
});
entity = _.cloneDeep(entity);
if (emptyPkFields.length > 0) {
this.toInsert = this.toInsert.filter(e => e !== entity);
this.toInsert.push(_.cloneDeep(entity));
await this.connector.upsert([entity], { action: "INSERT" });
} else {
this.toUpdate = this.toUpdate.filter(e => e !== entity);
this.toUpdate.push(_.cloneDeep(entity));
await this.connector.upsert([entity], { action: "UPDATE" });
}
localEventBus.publish("database:entities:saved", {
entities: [entity],
} as DatabaseEntitiesSavedEvent);
return this;
}
public remove(entity: EntityType, entityType?: EntityType): this {
public async remove(entity: EntityType, entityType?: EntityType): Promise<this> {
if (entityType) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
entity = _.merge(new (entityType as any)(), entity);
@@ -81,45 +78,13 @@ export default class EntityManager<EntityType extends Record<string, any>> {
if (!entity.constructor.prototype._entity || !entity.constructor.prototype._columns) {
throw Error("Cannot remove this object: it is not an entity.");
}
this.toRemove = this.toRemove.filter(e => e !== entity);
this.toRemove.push(_.cloneDeep(entity));
return this;
}
public async flush(): Promise<this> {
this.toInsert = _.uniqWith(this.toInsert, _.isEqual);
this.toUpdate = _.uniqWith(this.toUpdate, _.isEqual);
this.toRemove = _.uniqWith(this.toRemove, _.isEqual);
await this.connector.remove([entity]);
localEventBus.publish("database:entities:saved", {
entities: this.toInsert.map(e => _.cloneDeep(e)),
} as DatabaseEntitiesSavedEvent);
localEventBus.publish("database:entities:saved", {
entities: this.toUpdate.map(e => _.cloneDeep(e)),
} as DatabaseEntitiesSavedEvent);
localEventBus.publish("database:entities:saved", {
entities: this.toRemove.map(e => _.cloneDeep(e)),
entities: [entity],
} as DatabaseEntitiesRemovedEvent);
if (this.toInsert.length > 0) {
await this.connector.upsert(this.toInsert, { action: "INSERT" });
}
if (this.toUpdate.length > 0) {
await this.connector.upsert(this.toUpdate, { action: "UPDATE" });
}
if (this.toRemove.length > 0) {
await this.connector.remove(this.toRemove);
}
return this;
}
public reset(): void {
this.toInsert = [];
this.toUpdate = [];
this.toRemove = [];
}
}
@@ -135,25 +135,20 @@ export default class Repository<EntityType> {
}> {
if (previousValue === newValue)
throw new Error(`Previous and new values are identical: ${JSON.stringify(previousValue)}`);
await this.manager.flush().then(manager => manager.reset());
return this.connector.atomicCompareAndSet(entity, fieldName, previousValue, newValue);
}
async save(entity: EntityType, _context?: ExecutionContext): Promise<void> {
this.manager.persist(entity);
return this.manager.flush().then(manager => manager.reset());
await this.manager.persist(entity);
}
async saveAll(entities: EntityType[] = [], _context?: ExecutionContext): Promise<void> {
logger.debug("services.database.repository - Saving entities");
entities.forEach(entity => this.manager.persist(entity));
return this.manager.flush().then(manager => manager.reset());
await Promise.all(entities.map(entity => this.manager.persist(entity)));
}
async remove(entity: EntityType, _context?: ExecutionContext): Promise<void> {
this.manager.remove(entity);
return this.manager.flush().then(manager => manager.reset());
await this.manager.remove(entity);
}
//Avoid using this except when no choice
@@ -211,7 +211,12 @@ export default class MongoSearch extends SearchAdapter implements SearchAdapterI
logger.info(`Search query: ${JSON.stringify(query)}`);
console.log(query);
let cursor = collection.find(query).sort(sort);
const sortMapped: any = sort
? Object.fromEntries(
Object.entries(sort).map(([field, direction]) => [field, direction === "asc" ? 1 : -1]),
)
: {};
let cursor = collection.find(query).sort(sortMapped);
if (project) {
cursor = cursor.project(project);
}
@@ -14,6 +14,7 @@ export default {
access_entities: entity.access_info?.entities?.filter(e => e.level != "none").map(e => e.id),
last_modified: entity.last_modified,
mime_type: entity.last_version_cache?.file_metadata?.mime,
size: entity.last_version_cache?.file_metadata?.size,
}),
mongoMapping: {
text: {
@@ -29,10 +29,8 @@ import {
DriveFileAccessLevel,
DriveItemDetails,
DriveTdriveTab,
PaginateDocumentBody,
RootType,
SearchDocumentsOptions,
SortDocumentsBody,
TrashType,
} from "../types";
import {
@@ -62,6 +60,7 @@ import internal from "stream";
import config from "config";
import { MultipartFile } from "@fastify/multipart";
import { UploadOptions } from "src/services/files/types";
import { SortType } from "src/core/platform/services/search/api";
export class DocumentsService {
version: "1";
@@ -108,8 +107,6 @@ export class DocumentsService {
browse = async (
id: string,
options: SearchDocumentsOptions,
sort: SortDocumentsBody,
paginate: PaginateDocumentBody,
context: DriveExecutionContext & { public_token?: string },
): Promise<BrowseDetails> => {
if (isSharedWithMeFolder(id)) {
@@ -117,7 +114,7 @@ export class DocumentsService {
} else {
return {
nextPage: null,
...(await this.get(id, context, false, sort, paginate)),
...(await this.get(id, options, context, false)),
};
}
};
@@ -126,17 +123,23 @@ export class DocumentsService {
options: SearchDocumentsOptions,
context: DriveExecutionContext & { public_token?: string },
): Promise<BrowseDetails> => {
const result = [];
let fileList: ListResult<DriveFile>;
do {
fileList = await this.search(options, context);
result.push(...fileList.getEntities());
options.pagination = fileList.nextPage;
} while (fileList.nextPage?.page_token);
if (options.pagination) {
if (options.pagination.page_token == "1") {
delete options.pagination.page_token;
}
}
if (options.sort) {
options.sort = this.getSortFieldMapping(options.sort);
}
const fileList: ListResult<DriveFile> = await this.search(options, context);
const result = fileList.getEntities();
return {
access: "read",
children: result,
nextPage: null,
nextPage: fileList.nextPage,
path: [] as Array<DriveFile>,
};
};
@@ -158,10 +161,9 @@ export class DocumentsService {
*/
get = async (
id: string,
options: SearchDocumentsOptions,
context: DriveExecutionContext & { public_token?: string },
all?: boolean,
sort?: SortDocumentsBody,
paginate?: PaginateDocumentBody,
): Promise<DriveItemDetails> => {
if (!context) {
this.logger.error("invalid context");
@@ -214,24 +216,21 @@ export class DocumentsService {
)
).getEntities();
const sortFieldMapping = {
name: "name",
date: "last_modified",
size: "size",
};
const sortField = {};
sortField[sortFieldMapping[sort?.by] || "last_modified"] = sort?.order || "desc";
let sortField = {};
if (options?.sort) {
sortField = this.getSortFieldMapping(options.sort);
}
const dbType = await globalResolver.database.getConnector().getType();
// Initialize pagination
let pagination;
if (paginate) {
const { page, limit } = paginate;
const pageNumber = dbType === "mongodb" ? page : page / limit + 1;
if (options?.pagination) {
const { page_token, limitStr } = options.pagination;
const pageNumber =
dbType === "mongodb" ? parseInt(page_token) : parseInt(page_token) / parseInt(limitStr);
pagination = new Pagination(`${pageNumber}`, `${limit}`, false);
pagination = new Pagination(`${pageNumber}`, `${limitStr}`, false);
}
let children = isDirectory
@@ -443,7 +442,7 @@ export class DocumentsService {
);
// TODO: notify the user a document has been added to the directory shared with them
try {
if (driveItem.parent_id !== "root" && driveItem.parent_id !== "trash") {
if (!isVirtualFolder(driveItem.parent_id)) {
const parentItem = await this.repository.findOne(
{
id: driveItem.parent_id,
@@ -1100,7 +1099,7 @@ export class DocumentsService {
context: DriveExecutionContext,
): Promise<string> => {
for (const id of ids) {
const item = await this.get(id, context);
const item = await this.get(id, null, context);
if (!item) {
throw new CrudException("Drive item not found", 404);
}
@@ -1154,7 +1153,7 @@ export class DocumentsService {
size: number;
};
}> => {
const item = await this.get(id, context);
const item = await this.get(id, null, context);
if (item.item.is_directory) {
return { archive: await this.createZip([id], context) };
@@ -1386,4 +1385,16 @@ export class DocumentsService {
throw new CrudException(`Not enough space: ${size}, ${leftQuota}.`, 403);
}
};
getSortFieldMapping = (sort: SortType) => {
const sortFieldMapping = {
name: "name",
date: "last_modified",
size: "size",
};
const sortField = {};
sortField[sortFieldMapping[sort?.by] || "last_modified"] = sort?.order || "desc";
return sortField;
};
}
@@ -61,8 +61,8 @@ export type SearchDocumentsOptions = {
export type BrowseDocumentsOptions = {
filter?: SearchDocumentsBody;
sort?: SortDocumentsBody;
paginate?: PaginateDocumentBody;
sort?: SortType;
paginate?: Paginable;
};
export type SearchDocumentsBody = {
@@ -85,7 +85,7 @@ export type SortDocumentsBody = {
};
export type PaginateDocumentBody = {
page: number;
page?: string;
limit: number;
};
@@ -16,12 +16,10 @@ import {
DriveItemDetails,
DriveTdriveTab,
ItemRequestParams,
PaginateDocumentBody,
ItemRequestByEditingSessionKeyParams,
RequestParams,
SearchDocumentsBody,
SearchDocumentsOptions,
SortDocumentsBody,
} from "../../types";
import { DriveFileDTO } from "../dto/drive-file-dto";
import { DriveFileDTOBuilder } from "../../services/drive-file-dto-builder";
@@ -146,7 +144,7 @@ export class DocumentsController {
): Promise<DriveItemDetails> => {
const context = getDriveExecutionContext(request);
return await globalResolver.services.documents.documents.get(null, context);
return await globalResolver.services.documents.documents.get(null, null, context);
};
/**
@@ -165,7 +163,7 @@ export class DocumentsController {
const { id } = request.params;
return {
...(await globalResolver.services.documents.documents.get(id, context)),
...(await globalResolver.services.documents.documents.get(id, null, context)),
};
};
@@ -212,19 +210,12 @@ export class DocumentsController {
view: DriveFileDTOBuilder.VIEW_SHARED_WITH_ME,
onlyDirectlyShared: true,
onlyUploadedNotByMe: true,
sort: request.body.sort,
pagination: request.body.paginate,
};
const sortOptions: SortDocumentsBody = request.body.sort;
const paginateOptions: PaginateDocumentBody = request.body.paginate;
return {
...(await globalResolver.services.documents.documents.browse(
id,
options,
sortOptions,
paginateOptions,
context,
)),
...(await globalResolver.services.documents.documents.browse(id, options, context)),
};
};
@@ -551,12 +542,17 @@ export class DocumentsController {
);
if (ids[0] === "root") {
const items = await globalResolver.services.documents.documents.get(ids[0], context);
const items = await globalResolver.services.documents.documents.get(ids[0], null, context);
ids = items.children.map(item => item.id);
}
if (isDirectory === true) {
const items = await globalResolver.services.documents.documents.get(ids[0], context, true);
const items = await globalResolver.services.documents.documents.get(
ids[0],
null,
context,
true,
);
ids = items.children.map(item => item.id);
}
@@ -670,11 +666,16 @@ export class DocumentsController {
type: string;
};
}> {
const document = await globalResolver.services.documents.documents.get(req.body.document_id, {
public_token: req.body.token + (req.body.token_password ? "+" + req.body.token_password : ""),
user: null,
company: { id: req.body.company_id },
});
const document = await globalResolver.services.documents.documents.get(
req.body.document_id,
null,
{
public_token:
req.body.token + (req.body.token_password ? "+" + req.body.token_password : ""),
user: null,
company: { id: req.body.company_id },
},
);
if (!document || !document.access || document.access === "none")
throw new CrudException("You don't have access to this document", 401);