diff --git a/.run/Tests in e2e_.run.xml b/.run/Tests in e2e_.run.xml
new file mode 100644
index 00000000..ea3f0662
--- /dev/null
+++ b/.run/Tests in e2e_.run.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/the Drive feature.did search a file shared by another user.run.xml b/.run/the Drive feature.did search a file shared by another user.run.xml
new file mode 100644
index 00000000..402f7364
--- /dev/null
+++ b/.run/the Drive feature.did search a file shared by another user.run.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/the Drive feature.run.xml b/.run/the Drive feature.run.xml
new file mode 100644
index 00000000..2537ff97
--- /dev/null
+++ b/.run/the Drive feature.run.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tdrive/backend/node/config/test.json b/tdrive/backend/node/config/test.json
index 5918383c..851c4e70 100644
--- a/tdrive/backend/node/config/test.json
+++ b/tdrive/backend/node/config/test.json
@@ -19,5 +19,11 @@
"amqp": {
"urls": ["amqp://guest:guest@rabbitmq:5672"]
}
+ },
+ "search": {
+ "type": "elasticsearch",
+ "elasticsearch": {
+ "endpoint": "https://localhost:9200"
+ }
}
}
diff --git a/tdrive/backend/node/src/core/platform/framework/api/crud-service.ts b/tdrive/backend/node/src/core/platform/framework/api/crud-service.ts
index e0ed0e40..373e4943 100644
--- a/tdrive/backend/node/src/core/platform/framework/api/crud-service.ts
+++ b/tdrive/backend/node/src/core/platform/framework/api/crud-service.ts
@@ -149,6 +149,10 @@ export class CrudException extends Error {
static badGateway(details: string): CrudException {
return new CrudException(details, 502);
}
+
+ static throwMe(cause: Error, newOne: CrudException): void {
+ throw cause instanceof CrudException ? cause : newOne;
+ }
}
export interface Paginable {
@@ -202,7 +206,7 @@ export interface CRUDService(
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/mongodb/query-builder.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/mongodb/query-builder.ts
index decb7391..5efa386c 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/mongodb/query-builder.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/mongodb/query-builder.ts
@@ -49,7 +49,7 @@ export function buildComparison(where: any, options: FindOptions = {}): string[]
if (operator === "$gt" || operator === "$gte" || operator === "$lt" || operator === "$lte") {
(options[operator] || []).forEach(element => {
if (!where[element[0]]) where[element[0]] = {};
- where[element[0]][operator] = element[1];
+ where[element[0]][operator] = parseInt(element[1]);
});
}
});
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/repository/repository.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/repository/repository.ts
index 51b9cd96..374fcef1 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/repository/repository.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/repository/repository.ts
@@ -16,9 +16,9 @@ export type FindFilter = { [key: string]: any };
export type RemoveFilter = { [key: string]: any };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-type comparisonType = [string, any];
+export type comparisonType = [string, any];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-type inType = [string, Array];
+export type inType = [string, Array];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type likeType = [string, any];
diff --git a/tdrive/backend/node/src/core/platform/services/search/adapters/elasticsearch/index.ts b/tdrive/backend/node/src/core/platform/services/search/adapters/elasticsearch/index.ts
index 342ff301..01835399 100644
--- a/tdrive/backend/node/src/core/platform/services/search/adapters/elasticsearch/index.ts
+++ b/tdrive/backend/node/src/core/platform/services/search/adapters/elasticsearch/index.ts
@@ -42,6 +42,9 @@ export default class ElasticSearch extends SearchAdapter implements SearchAdapte
try {
this.client = new Client({
node: this.configuration.endpoint,
+ ssl: {
+ rejectUnauthorized: false,
+ },
});
} catch (e) {
logger.error(`Unable to connect to ElasticSearch at ${this.configuration.endpoint}`);
diff --git a/tdrive/backend/node/src/core/platform/services/search/adapters/elasticsearch/search.ts b/tdrive/backend/node/src/core/platform/services/search/adapters/elasticsearch/search.ts
index 5446149d..bfd2c7c7 100644
--- a/tdrive/backend/node/src/core/platform/services/search/adapters/elasticsearch/search.ts
+++ b/tdrive/backend/node/src/core/platform/services/search/adapters/elasticsearch/search.ts
@@ -3,6 +3,7 @@ import { TransportRequestOptions } from "@elastic/elasticsearch/lib/Transport";
import { logger } from "../../../../../../core/platform/framework/logger";
import { EntityTarget, FindFilter, FindOptions, getEntityDefinition } from "../../api";
import { asciiFold } from "../utils";
+import { comparisonType } from "src/core/platform/services/database/services/orm/repository/repository";
export function buildSearchQuery(
entityType: EntityTarget,
@@ -45,7 +46,44 @@ export function buildSearchQuery(
}
}
- //TODO implement $gte, $lt, etc
+ function buildRangeQuery(
+ lteOperations: comparisonType[],
+ key: "gte" | "lte" | "lt" | "gt",
+ ): void {
+ for (const lteOperation of lteOperations) {
+ if (lteOperation?.length == 2 && lteOperation[0]) {
+ const field_name = lteOperation[0];
+ esBody.query.bool.must.push({
+ range: {
+ [field_name]: {
+ [key]: lteOperation[1] ? parseInt(lteOperation[1]) : 0,
+ },
+ },
+ });
+ } else {
+ logger.warn(`Not enough data to include to the query: ${lteOperation}`);
+ }
+ }
+ }
+
+ if (options.$lte?.length) {
+ esBody.query.bool.must = esBody.query.bool.must || [];
+ buildRangeQuery(options.$lte, "lte");
+ }
+
+ if (options.$gte?.length) {
+ esBody.query.bool.must = esBody.query.bool.must || [];
+ buildRangeQuery(options.$gte, "gte");
+ }
+
+ if (options.$lt?.length) {
+ esBody.query.bool.must = esBody.query.bool.must || [];
+ buildRangeQuery(options.$lt, "lt");
+ }
+ if (options.$gt?.length) {
+ esBody.query.bool.must = esBody.query.bool.must || [];
+ buildRangeQuery(options.$gt, "gt");
+ }
if (options.$text) {
esBody.query.bool.minimum_should_match = 1;
diff --git a/tdrive/backend/node/src/core/platform/services/search/adapters/mongosearch/index.ts b/tdrive/backend/node/src/core/platform/services/search/adapters/mongosearch/index.ts
index 302cb0d5..fc513d60 100644
--- a/tdrive/backend/node/src/core/platform/services/search/adapters/mongosearch/index.ts
+++ b/tdrive/backend/node/src/core/platform/services/search/adapters/mongosearch/index.ts
@@ -84,7 +84,9 @@ export default class MongoSearch extends SearchAdapter implements SearchAdapterI
//Create one index for each type of indexes ["text"]
Object.keys(indexedFields).forEach(k => {
- collection.createIndex(indexedFields[k], { default_language: "none" });
+ collection.createIndex(indexedFields[k], {
+ default_language: "none",
+ });
});
}
diff --git a/tdrive/backend/node/src/core/platform/services/search/adapters/mongosearch/search.ts b/tdrive/backend/node/src/core/platform/services/search/adapters/mongosearch/search.ts
index fa9d68f0..4db7af22 100644
--- a/tdrive/backend/node/src/core/platform/services/search/adapters/mongosearch/search.ts
+++ b/tdrive/backend/node/src/core/platform/services/search/adapters/mongosearch/search.ts
@@ -58,6 +58,10 @@ export function buildSearchQuery(
});
}
+ if (options.$sort) {
+ sort = options.$sort;
+ }
+
return {
project,
query,
diff --git a/tdrive/backend/node/src/core/platform/services/search/api.ts b/tdrive/backend/node/src/core/platform/services/search/api.ts
index e64d574d..12ef3cdd 100644
--- a/tdrive/backend/node/src/core/platform/services/search/api.ts
+++ b/tdrive/backend/node/src/core/platform/services/search/api.ts
@@ -26,7 +26,7 @@ type TextType = {
$diacriticSensitive?: boolean; //Default false
};
-type SortType = {
+export type SortType = {
[key: string]: "asc" | "desc";
};
diff --git a/tdrive/backend/node/src/services/documents/entities/drive-file.search.ts b/tdrive/backend/node/src/services/documents/entities/drive-file.search.ts
index 722b4a42..d1e6a449 100644
--- a/tdrive/backend/node/src/services/documents/entities/drive-file.search.ts
+++ b/tdrive/backend/node/src/services/documents/entities/drive-file.search.ts
@@ -8,43 +8,36 @@ export default {
creator: entity.creator,
added: entity.added,
name: entity.name,
+ //especially for ES because it doesn't allow to sort by not a keyword fields
+ name_keyword: entity.name,
company_id: entity.company_id,
- access_users: ["user_1234", "user_454"],
- access_users_x_initiator: ["user_1234_x_user_4343", "user_1234_x_user_4343"],
-
- access_entities: [
- {
- type: "user",
- level: "read",
- target: "user_1234",
- source: "user_5678",
- },
- {
- type: "user",
- level: "read",
- target: "user_abcd",
- source: "user_5678",
- },
- ],
+ 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,
}),
mongoMapping: {
text: {
content_keywords: "text",
tags: "text",
creator: "text",
- added: "text",
name: "text",
+ name_keyword: "text",
company_id: "text",
+ mime_type: "text",
},
},
esMapping: {
properties: {
name: { type: "text", index_prefixes: { min_chars: 1 } },
+ name_keyword: { type: "keyword" },
content_keywords: { type: "text", index_prefixes: { min_chars: 1 } },
tags: { type: "keyword" },
creator: { type: "keyword" },
- added: { type: "keyword" },
+ added: { type: "unsigned_long" },
company_id: { type: "keyword" },
+ access_entities: { type: "keyword" },
+ mime_type: { type: "keyword" },
+ last_modified: { type: "unsigned_long" },
},
},
};
diff --git a/tdrive/backend/node/src/services/documents/entities/drive-file.ts b/tdrive/backend/node/src/services/documents/entities/drive-file.ts
index fda31453..bdbc0dcd 100644
--- a/tdrive/backend/node/src/services/documents/entities/drive-file.ts
+++ b/tdrive/backend/node/src/services/documents/entities/drive-file.ts
@@ -48,13 +48,13 @@ export class DriveFile {
@Column("tags", "encoded_json")
tags: string[];
- @Type(() => String)
- @Column("added", "string")
- added: string;
+ @Type(() => Number)
+ @Column("added", "number")
+ added: number;
- @Type(() => String)
- @Column("last_modified", "string")
- last_modified: string;
+ @Type(() => Number)
+ @Column("last_modified", "number")
+ last_modified: number;
@Column("access_info", "encoded_json")
access_info: AccessInformation;
diff --git a/tdrive/backend/node/src/services/documents/services/index.ts b/tdrive/backend/node/src/services/documents/services/index.ts
index 922fa187..85caed6e 100644
--- a/tdrive/backend/node/src/services/documents/services/index.ts
+++ b/tdrive/backend/node/src/services/documents/services/index.ts
@@ -1,7 +1,10 @@
import SearchRepository from "../../../core/platform/services/search/repository";
import { getLogger, logger, TdriveLogger } from "../../../core/platform/framework";
import { CrudException, ListResult } from "../../../core/platform/framework/api/crud-service";
-import Repository from "../../../core/platform/services/database/services/orm/repository/repository";
+import Repository, {
+ inType,
+ comparisonType,
+} from "../../../core/platform/services/database/services/orm/repository/repository";
import { PublicFile } from "../../../services/files/entities/file";
import globalResolver from "../../../services/global-resolver";
import { hasCompanyAdminLevel } from "../../../utils/company";
@@ -291,9 +294,9 @@ export class DocumentsService {
await this.repository.save(driveItem);
await updateItemSize(driveItem.parent_id, this.repository, context);
- this.notifyWebsocket(driveItem.parent_id, context);
+ await this.notifyWebsocket(driveItem.parent_id, context);
- globalResolver.platformServices.messageQueue.publish(
+ await globalResolver.platformServices.messageQueue.publish(
"services:documents:process",
{
data: {
@@ -307,7 +310,7 @@ export class DocumentsService {
return driveItem;
} catch (error) {
this.logger.error("Failed to create drive item", error);
- throw new CrudException("Failed to create item", 500);
+ CrudException.throwMe(error, new CrudException("Failed to create item", 500));
}
};
@@ -768,12 +771,39 @@ export class DocumentsService {
pagination: {
limitStr: "100",
},
- ...(options.company_id ? { $in: [["company_id", [options.company_id]]] } : {}),
- ...(options.creator ? { $in: [["creator", [options.creator]]] } : {}),
- ...(options.added ? { $in: [["added", [options.added]]] } : {}),
- $text: {
- $search: options.search,
- },
+ $in: [
+ ["access_entities", [context.user.id, context.company.id]],
+ ...(options.company_id ? [["company_id", [options.company_id]] as inType] : []),
+ ...(options.creator ? [["creator", [options.creator]] as inType] : []),
+ ...(options.mime_type
+ ? [
+ [
+ "mime_type",
+ Array.isArray(options.mime_type) ? options.mime_type : [options.mime_type],
+ ] as inType,
+ ]
+ : []),
+ ],
+ $lte: [
+ ...(options.last_modified_lt
+ ? [["last_modified", options.last_modified_lt] as comparisonType]
+ : []),
+ ...(options.added_lt ? [["added", options.added_lt] as comparisonType] : []),
+ ],
+ $gte: [
+ ...(options.last_modified_gt
+ ? [["last_modified", options.last_modified_gt] as comparisonType]
+ : []),
+ ...(options.added_gt ? [["added", options.added_gt] as comparisonType] : []),
+ ],
+ ...(options.search
+ ? {
+ $text: {
+ $search: options.search,
+ },
+ }
+ : {}),
+ ...(options.sort ? { $sort: options.sort } : {}),
},
context,
);
diff --git a/tdrive/backend/node/src/services/documents/types.ts b/tdrive/backend/node/src/services/documents/types.ts
index 2d77c6bc..81c8b86d 100644
--- a/tdrive/backend/node/src/services/documents/types.ts
+++ b/tdrive/backend/node/src/services/documents/types.ts
@@ -1,6 +1,7 @@
import { ExecutionContext } from "../../core/platform/framework/api/crud-service";
import { DriveFile } from "./entities/drive-file";
import { FileVersion } from "./entities/file-version";
+import { SortType } from "src/core/platform/services/search/api";
export interface CompanyExecutionContext extends ExecutionContext {
company: { id: string };
@@ -40,14 +41,24 @@ export type SearchDocumentsOptions = {
search?: string;
company_id?: string;
creator?: string;
- added?: string;
+ added_gt?: number;
+ added_lt?: number;
+ mime_type?: Array;
+ last_modified_gt?: number;
+ last_modified_lt?: number;
+ sort?: SortType;
};
export type SearchDocumentsBody = {
search?: string;
company_id?: string;
creator?: string;
- added?: string;
+ added_gt?: number;
+ added_lt?: number;
+ mime_type?: [string];
+ last_modified_gt?: number;
+ last_modified_lt?: number;
+ sort?: SortType;
};
export type DocumentsMessageQueueRequest = {
diff --git a/tdrive/backend/node/src/services/documents/utils.ts b/tdrive/backend/node/src/services/documents/utils.ts
index aa82ee81..6e5a9be1 100644
--- a/tdrive/backend/node/src/services/documents/utils.ts
+++ b/tdrive/backend/node/src/services/documents/utils.ts
@@ -47,11 +47,11 @@ export const getDefaultDriveItem = (
): DriveFile => {
const defaultDriveItem = merge>(new DriveFile(), {
company_id: context.company.id,
- added: item.added || new Date().getTime().toString(),
+ added: item.added || new Date().getTime(),
creator: item.creator || context.user?.id,
is_directory: item.is_directory || false,
is_in_trash: item.is_in_trash || false,
- last_modified: new Date().getTime().toString(),
+ last_modified: new Date().getTime(),
parent_id: item.parent_id || "root",
content_keywords: item.content_keywords || "",
description: item.description || "",
@@ -422,7 +422,9 @@ export const getFileMetadata = async (
* Finds a suitable name for an item based on items inside the same folder.
*
* @param {string} parent_id - the parent id.
+ * @param id
* @param {string} name - the item name.
+ * @param is_directory
* @param {Repository} repository - the drive repository.
* @param {CompanyExecutionContext} context - the execution context.
* @returns {Promise} - the drive item name.
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 875cec45..acfd4057 100644
--- a/tdrive/backend/node/src/services/documents/web/controllers/documents.ts
+++ b/tdrive/backend/node/src/services/documents/web/controllers/documents.ts
@@ -65,7 +65,7 @@ export class DocumentsController {
);
} catch (error) {
logger.error("Failed to create Drive item", error);
- throw new CrudException("Failed to create Drive item", 500);
+ CrudException.throwMe(error, new CrudException("Failed to create Drive item", 500));
}
};
@@ -73,6 +73,7 @@ export class DocumentsController {
* Deletes a DriveFile item or empty the trash or delete root folder contents
*
* @param {FastifyRequest} request
+ * @param {FastifyReply} reply
* @returns {Promise}
*/
delete = async (
@@ -227,7 +228,7 @@ export class DocumentsController {
* If the item is a folder, a zip will be automatically generated.
*
* @param {FastifyRequest} request
- * @param {FastifyReply} reply
+ * @param {FastifyReply} response
*/
download = async (
request: FastifyRequest<{
@@ -342,26 +343,27 @@ export class DocumentsController {
): Promise> => {
try {
const context = getDriveExecutionContext(request);
- const { search = "", added = "", company_id = "", creator = "" } = request.body;
const options: SearchDocumentsOptions = {
- company_id: company_id || context.company.id,
- ...(search ? { search } : {}),
- ...(added ? { added } : {}),
- ...(creator ? { creator } : {}),
+ ...request.body,
+ company_id: request.body.company_id || context.company.id,
};
if (!Object.keys(options).length) {
- throw Error("Search options are empty");
+ this.throw500Search();
}
return await globalResolver.services.documents.documents.search(options, context);
} catch (error) {
logger.error("error while searching for document", error);
- throw new CrudException("Failed to search for documents", 500);
+ this.throw500Search();
}
};
+ private throw500Search() {
+ throw new CrudException("Failed to search for documents", 500);
+ }
+
getTab = async (
request: FastifyRequest<{
Params: { tab_id: string; company_id: string };
diff --git a/tdrive/backend/node/src/services/files/services/index.ts b/tdrive/backend/node/src/services/files/services/index.ts
index cb422ee6..0f6fe021 100644
--- a/tdrive/backend/node/src/services/files/services/index.ts
+++ b/tdrive/backend/node/src/services/files/services/index.ts
@@ -187,6 +187,9 @@ export class FileServiceImpl {
},
): Promise {
let entity = await this.repository.findOne(pk, {}, context);
+ if (entity == null) {
+ throw new CrudException(`File not found ${pk.company_id}|${pk.id}`, 404);
+ }
if (options?.waitForThumbnail) {
for (let i = 1; i < 100; i++) {
diff --git a/tdrive/backend/node/src/utils/types.ts b/tdrive/backend/node/src/utils/types.ts
index 1f36f642..015fe5b1 100644
--- a/tdrive/backend/node/src/utils/types.ts
+++ b/tdrive/backend/node/src/utils/types.ts
@@ -103,7 +103,9 @@ export interface ResourceEventsPayload {
}
export interface PaginationQueryParameters {
+ // It is offset for MongoDB and pointer to the next page for ScyllaDB
page_token?: string;
+ // Page size
limit?: string;
websockets?: boolean;
}
diff --git a/tdrive/backend/node/test/e2e/files/assets/sample.doc b/tdrive/backend/node/test/e2e/common/assets/sample.doc
similarity index 100%
rename from tdrive/backend/node/test/e2e/files/assets/sample.doc
rename to tdrive/backend/node/test/e2e/common/assets/sample.doc
diff --git a/tdrive/backend/node/test/e2e/files/assets/sample.gif b/tdrive/backend/node/test/e2e/common/assets/sample.gif
similarity index 100%
rename from tdrive/backend/node/test/e2e/files/assets/sample.gif
rename to tdrive/backend/node/test/e2e/common/assets/sample.gif
diff --git a/tdrive/backend/node/test/e2e/files/assets/sample.mp4 b/tdrive/backend/node/test/e2e/common/assets/sample.mp4
similarity index 100%
rename from tdrive/backend/node/test/e2e/files/assets/sample.mp4
rename to tdrive/backend/node/test/e2e/common/assets/sample.mp4
diff --git a/tdrive/backend/node/test/e2e/files/assets/sample.pdf b/tdrive/backend/node/test/e2e/common/assets/sample.pdf
similarity index 100%
rename from tdrive/backend/node/test/e2e/files/assets/sample.pdf
rename to tdrive/backend/node/test/e2e/common/assets/sample.pdf
diff --git a/tdrive/backend/node/test/e2e/files/assets/sample.png b/tdrive/backend/node/test/e2e/common/assets/sample.png
similarity index 100%
rename from tdrive/backend/node/test/e2e/files/assets/sample.png
rename to tdrive/backend/node/test/e2e/common/assets/sample.png
diff --git a/tdrive/backend/node/test/e2e/files/assets/sample.zip b/tdrive/backend/node/test/e2e/common/assets/sample.zip
similarity index 100%
rename from tdrive/backend/node/test/e2e/files/assets/sample.zip
rename to tdrive/backend/node/test/e2e/common/assets/sample.zip
diff --git a/tdrive/backend/node/test/e2e/common/common_test_helpers.ts b/tdrive/backend/node/test/e2e/common/common_test_helpers.ts
new file mode 100644
index 00000000..b71f7644
--- /dev/null
+++ b/tdrive/backend/node/test/e2e/common/common_test_helpers.ts
@@ -0,0 +1,192 @@
+// @ts-ignore
+import fs from "fs";
+import {ResourceUpdateResponse, Workspace} from "../../../src/utils/types";
+import {File} from "../../../src/services/files/entities/file";
+import {deserialize} from "class-transformer";
+import formAutoContent from "form-auto-content";
+import {TestPlatform, User} from "../setup";
+import {v1 as uuidv1} from "uuid";
+import {TestDbService} from "../utils.prepare.db";
+import {DriveFile} from "../../../src/services/documents/entities/drive-file";
+import {FileVersion} from "../../../src/services/documents/entities/file-version";
+import {SearchResultMockClass} from "./entities/mock_entities";
+import {logger} from "../../../src/core/platform/framework";
+
+export default class TestHelpers {
+
+ private static readonly DOC_URL = "/internal/services/documents/v1";
+
+ static readonly ALL_FILES = [
+ "sample.png",
+ "sample.gif",
+ "sample.pdf",
+ "sample.doc",
+ "sample.zip",
+ "sample.mp4",
+ ]
+
+ platform: TestPlatform;
+ dbService: TestDbService;
+ user: User;
+ workspace: Workspace;
+ jwt: string;
+
+ private constructor(
+ platform: TestPlatform,
+ ) {
+ this.platform = platform
+ }
+
+ private async init(newUser: boolean) {
+ this.dbService = await TestDbService.getInstance(this.platform, true);
+ if (newUser) {
+ this.workspace = this.platform.workspace;
+ const workspacePK = {id: this.workspace.workspace_id, company_id: this.workspace.company_id};
+ this.user = await this.dbService.createUser([workspacePK], {}, uuidv1());
+ } else {
+ this.user = this.platform.currentUser;
+ this.workspace = this.platform.workspace;
+ }
+ this.jwt = this.getJWTTokenForUser(this.user.id);
+ }
+
+ public static async getInstance(platform: TestPlatform, newUser = false): Promise {
+ const helpers = new TestHelpers(platform);
+ await helpers.init(newUser)
+ return helpers;
+ }
+
+ async uploadFiles() {
+ return Promise.all(TestHelpers.ALL_FILES.map(f => this.uploadFile(f)));
+ }
+
+ async uploadRandomFile() {
+ return await this.uploadFile(TestHelpers.ALL_FILES[Math.floor((Math.random()*TestHelpers.ALL_FILES.length))])
+ }
+
+ async uploadFile(filename: string) {
+ logger.info(`Upload ${filename} for the user: ${this.user.id}`);
+ const fullPath = `${__dirname}/assets/${filename}`
+ const url = "/internal/services/files/v1";
+ const form = formAutoContent({file: fs.createReadStream(fullPath)});
+ form.headers["authorization"] = `Bearer ${this.jwt}`;
+
+ const filesUploadRaw = await this.platform.app.inject({
+ method: "POST",
+ url: `${url}/companies/${this.platform.workspace.company_id}/files?thumbnail_sync=1`,
+ ...form,
+ });
+
+ const filesUpload: ResourceUpdateResponse = deserialize(
+ ResourceUpdateResponse,
+ filesUploadRaw.body,
+ );
+ return filesUpload.resource;
+ }
+
+ private getJWTTokenForUser(userId: string): string {
+ const payload = {
+ sub: userId,
+ role: "",
+ }
+ return this.platform.authService.sign(payload);
+ }
+
+ async uploadFileAndCreateDocument(
+ filename: string
+ ) {
+ return this.uploadFile(filename).then(f => this.createDocumentFromFile(f));
+ };
+
+ async uploadRandomFileAndCreateDocument() {
+ return this.uploadRandomFile().then(f => this.createDocumentFromFile(f));
+ };
+
+ async uploadAllFilesAndCreateDocuments() {
+ return await Promise.all(TestHelpers.ALL_FILES.map(f => this.uploadFileAndCreateDocument(f)))
+ };
+
+ async uploadAllFilesOneByOne() {
+ const files: Array = [];
+ for (const idx in TestHelpers.ALL_FILES) {
+ const f = await this.uploadFile(TestHelpers.ALL_FILES[idx]);
+ const doc = await this.createDocumentFromFile(f);
+ files.push(doc);
+ }
+ return files;
+ };
+
+ async createDocument(
+ platform: TestPlatform,
+ item: Partial,
+ version: Partial
+ ) {
+
+ return await platform.app.inject({
+ method: "POST",
+ url: `${TestHelpers.DOC_URL}/companies/${platform.workspace.company_id}/item`,
+ headers: {
+ authorization: `Bearer ${this.jwt}`,
+ },
+ payload: {
+ item,
+ version,
+ },
+ });
+ };
+
+ async createDocumentFromFile(
+ file: File
+ ) {
+ const item = {
+ name: file.metadata.name,
+ parent_id: "root",
+ company_id: file.company_id,
+ };
+
+ const version = {
+ file_metadata: {
+ name: file.metadata.name,
+ size: file.upload_data?.size,
+ thumbnails: [],
+ external_id: file.id
+ }
+ }
+
+ const response = await this.createDocument(this.platform, item, version);
+ return deserialize(DriveFile, response.body);
+ };
+
+ async updateDocument(
+ id: string | "root" | "trash",
+ item: Partial
+ ) {
+ return await this.platform.app.inject({
+ method: "POST",
+ url: `${TestHelpers.DOC_URL}/companies/${this.platform.workspace.company_id}/item/${id}`,
+ headers: {
+ authorization: `Bearer ${this.jwt}`,
+ },
+ payload: item,
+ });
+ };
+
+ async searchDocument (
+ payload: Record
+ ){
+ const response = await this.platform.app.inject({
+ method: "POST",
+ url: `${TestHelpers.DOC_URL}/companies/${this.platform.workspace.company_id}/search`,
+ headers: {
+ authorization: `Bearer ${this.jwt}`,
+ },
+ payload,
+ });
+
+ return deserialize(
+ SearchResultMockClass,
+ response.body)
+ };
+
+}
+
diff --git a/tdrive/backend/node/test/e2e/common/entities/mock_entities.ts b/tdrive/backend/node/test/e2e/common/entities/mock_entities.ts
new file mode 100644
index 00000000..fea587ba
--- /dev/null
+++ b/tdrive/backend/node/test/e2e/common/entities/mock_entities.ts
@@ -0,0 +1,42 @@
+import {DriveFileAccessLevel, publicAccessLevel} from "../../../../src/services/documents/types";
+
+export type MockAccessInformation = {
+ public?: {
+ token: string;
+ password: string;
+ expiration: number;
+ level: publicAccessLevel;
+ };
+ entities: MockAuthEntity[];
+};
+
+export type MockAuthEntity = {
+ type: "user" | "channel" | "company" | "folder";
+ id: string | "parent";
+ level: publicAccessLevel | DriveFileAccessLevel;
+};
+
+export class DriveFileMockClass {
+ id: string;
+ name: string;
+ size: number;
+ added: string;
+ parent_id: string;
+ extension: string;
+ description: string;
+ tags: string[];
+ last_modified: string;
+ access_info: MockAccessInformation;
+ creator: string;
+}
+
+export class DriveItemDetailsMockClass {
+ path: string[];
+ item: DriveFileMockClass;
+ children: DriveFileMockClass[];
+ versions: Record[];
+}
+
+export class SearchResultMockClass {
+ entities: DriveFileMockClass[];
+}
\ No newline at end of file
diff --git a/tdrive/backend/node/test/e2e/documents/documents.spec.ts b/tdrive/backend/node/test/e2e/documents/documents.spec.ts
index 51537b3e..42bc5c24 100644
--- a/tdrive/backend/node/test/e2e/documents/documents.spec.ts
+++ b/tdrive/backend/node/test/e2e/documents/documents.spec.ts
@@ -1,5 +1,5 @@
import { describe, beforeEach, afterEach, it, expect, afterAll } from "@jest/globals";
-import { deserialize } from "class-transformer";
+import {deserialize} from "class-transformer";
import { File } from "../../../src/services/files/entities/file";
import { ResourceUpdateResponse } from "../../../src/utils/types";
import { init, TestPlatform } from "../setup";
@@ -13,28 +13,13 @@ import {
e2e_searchDocument,
e2e_updateDocument,
} from "./utils";
+import TestHelpers from "../common/common_test_helpers";
+import {DriveFileMockClass, DriveItemDetailsMockClass, SearchResultMockClass} from "../common/entities/mock_entities";
describe("the Drive feature", () => {
let platform: TestPlatform;
-
- class DriveFileMockClass {
- id: string;
- name: string;
- size: number;
- added: string;
- parent_id: string;
- }
-
- class DriveItemDetailsMockClass {
- path: string[];
- item: DriveFileMockClass;
- children: DriveFileMockClass[];
- versions: Record[];
- }
-
- class SearchResultMockClass {
- entities: DriveFileMockClass[];
- }
+ let currentUser: TestHelpers;
+ let dbService: TestDbService;
beforeEach(async () => {
platform = await init({
@@ -59,6 +44,8 @@ describe("the Drive feature", () => {
"documents",
],
});
+ currentUser = await TestHelpers.getInstance(platform);
+ dbService = await TestDbService.getInstance(platform, true);
});
afterEach(async () => {
@@ -170,8 +157,8 @@ describe("the Drive feature", () => {
done?.();
});
- // TODO: wait for elastic search index
it("did search for an item", async done => {
+ jest.setTimeout(10000);
const createItemResult = await createItem();
expect(createItemResult.id).toBeDefined();
@@ -237,4 +224,230 @@ describe("the Drive feature", () => {
done?.();
});
+
+ it("did search by mime type", async done => {
+ // given:: all the sample files uploaded and documents for them created
+ await Promise.all((await currentUser.uploadFiles()).map(f => currentUser.createDocumentFromFile(f)))
+
+ const filters = {
+ mime_type: "application/pdf",
+ };
+
+ jest.setTimeout(10000);
+ await new Promise(r => setTimeout(r, 5000));
+
+
+ let documents = await currentUser.searchDocument(filters);
+ expect(documents.entities).toHaveLength(1);
+
+ const actualFile = documents.entities[0];
+ expect(actualFile.name).toEqual("sample.pdf");
+
+ done?.();
+ });
+
+ it("did search by last modified", async done => {
+ jest.setTimeout(10000);
+ const user = await TestHelpers.getInstance(platform, true);
+ // given:: all the sample files uploaded and documents for them created
+ const start = new Date().getTime();
+ await user.uploadAllFilesOneByOne()
+ const end = new Date().getTime();
+ await user.uploadAllFilesOneByOne()
+ //wait for putting docs to elastic and its indexing
+ await new Promise(r => setTimeout(r, 3000));
+
+ //then:: all the files are searchable without filters
+ let documents = await user.searchDocument({});
+ expect(documents.entities).toHaveLength(TestHelpers.ALL_FILES.length * 2);
+
+ //then:: only file uploaded in the [start, end] interval are shown in the search results
+ const filters = {
+ last_modified_gt: start.toString(),
+ last_modified_lt: end.toString()
+ };
+ documents = await user.searchDocument(filters);
+ expect(documents.entities).toHaveLength(TestHelpers.ALL_FILES.length);
+
+ done?.();
+ });
+
+ it("did search a file shared by another user", async done => {
+ jest.setTimeout(30000);
+ //given:
+ const oneUser = await TestHelpers.getInstance(platform, true);
+ const anotherUser = await TestHelpers.getInstance(platform, true);
+ //upload files
+ let files = await oneUser.uploadAllFilesOneByOne()
+
+ await new Promise(r => setTimeout(r, 5000));
+
+ //then:: files are not searchable for user without permissions
+ expect((await anotherUser.searchDocument({})).entities).toHaveLength(0);
+
+ //and searchable for user that have
+ expect((await oneUser.searchDocument({})).entities).toHaveLength(TestHelpers.ALL_FILES.length);
+
+ //give permissions to the file
+ files[0].access_info.entities.push({
+ type: "user",
+ id: anotherUser.user.id,
+ level: "read"
+ })
+ await oneUser.updateDocument(files[0].id, files[0]);
+ await new Promise(r => setTimeout(r, 3000));
+
+ //then file become searchable
+ expect((await anotherUser.searchDocument({})).entities).toHaveLength(1);
+
+ done?.();
+ });
+
+ it("did search a file by file owner", async done => {
+ jest.setTimeout(30000);
+ //given:
+ const oneUser = await TestHelpers.getInstance(platform, true);
+ const anotherUser = await TestHelpers.getInstance(platform, true);
+ //upload files
+ let files = await oneUser.uploadAllFilesOneByOne()
+ await anotherUser.uploadAllFilesOneByOne()
+ //give permissions for all files to 'another user'
+ await Promise.all(files.map(f => {
+ f.access_info.entities.push({
+ type: "user",
+ id: anotherUser.user.id,
+ level: "read"
+ })
+ return oneUser.updateDocument(f.id, f);
+ }));
+
+ await new Promise(r => setTimeout(r, 5000));
+
+ //then:: all files are searchable for 'another user'
+ expect((await anotherUser.searchDocument({})).entities).toHaveLength(TestHelpers.ALL_FILES.length * 2);
+
+ //and searchable for user that have
+ expect((await oneUser.searchDocument({
+ creator: oneUser.user.id,
+ })).entities).toHaveLength(TestHelpers.ALL_FILES.length);
+
+ done?.();
+
+ });
+
+ it("did search by 'added' date", async done => {
+ jest.setTimeout(10000);
+ const user = await TestHelpers.getInstance(platform, true);
+ // given:: all the sample files uploaded and documents for them created
+ await user.uploadRandomFileAndCreateDocument();
+ const start = new Date().getTime();
+ await user.uploadAllFilesAndCreateDocuments()
+ const end = new Date().getTime();
+ await user.uploadRandomFileAndCreateDocument();
+ //wait for putting docs to elastic and its indexing
+ await new Promise(r => setTimeout(r, 3000));
+
+ //then:: all the files are searchable without filters
+ let documents = await user.searchDocument({});
+ expect(documents.entities).toHaveLength(TestHelpers.ALL_FILES.length + 2);
+
+ //then:: only file uploaded in the [start, end] interval are shown in the search results
+ const filters = {
+ added_gt: start.toString(),
+ added_lt: end.toString()
+ };
+ documents = await user.searchDocument(filters);
+ expect(documents.entities).toHaveLength(TestHelpers.ALL_FILES.length);
+
+ done?.();
+ });
+
+ it("did search order by name", async done => {
+ jest.setTimeout(10000);
+ const user = await TestHelpers.getInstance(platform, true);
+ // given:: all the sample files uploaded and documents for them created
+ await user.uploadAllFilesAndCreateDocuments()
+ //wait for putting docs to elastic and its indexing
+ await new Promise(r => setTimeout(r, 5000));
+
+ //when:: sort files by name is ascending order
+ const options = {
+ sort: {
+ name_keyword: "asc",
+ }
+ };
+ const documents = await user.searchDocument(options);
+
+ //then all the files are sorted properly by name
+ expect(documents.entities.map(e => e.name)).toEqual(TestHelpers.ALL_FILES.sort());
+ done?.();
+ });
+
+ it("did search order by name desc", async done => {
+ jest.setTimeout(10000);
+ const user = await TestHelpers.getInstance(platform, true);
+ // given:: all the sample files uploaded and documents for them created
+ await user.uploadAllFilesOneByOne()
+ //wait for putting docs to elastic and its indexing
+ await new Promise(r => setTimeout(r, 5000));
+
+ //when:: sort files by name is ascending order
+ const options = {
+ sort: {
+ name_keyword: "desc",
+ }
+ };
+ const documents = await user.searchDocument(options);
+
+ //then all the files are sorted properly by name
+ expect(documents.entities.map(e => e.name)).toEqual(TestHelpers.ALL_FILES.sort().reverse());
+ done?.();
+ });
+
+ it("did search order by added date", async done => {
+ jest.setTimeout(10000);
+ const user = await TestHelpers.getInstance(platform, true);
+ // given:: all the sample files uploaded and documents for them created
+ await user.uploadAllFilesOneByOne();
+ //wait for putting docs to elastic and its indexing
+ await new Promise(r => setTimeout(r, 5000));
+
+ //when:: ask to sort files by the 'added' field
+ const options = {
+ sort: {
+ added: "asc",
+ }
+ };
+ const documents = await user.searchDocument(options);
+
+ //then:: files should be sorted properly
+ expect(documents.entities.map(e => e.name)).toEqual(TestHelpers.ALL_FILES);
+
+ done?.();
+ });
+
+ it("did search order by added date desc", async done => {
+ jest.setTimeout(10000);
+ const user = await TestHelpers.getInstance(platform, true);
+ // given:: all the sample files uploaded and documents for them created
+ await user.uploadAllFilesOneByOne();
+ //wait for putting docs to elastic and its indexing
+ await new Promise(r => setTimeout(r, 5000));
+
+ //when:: ask to sort files by the 'added' field desc
+ const options = {
+ sort: {
+ added: "desc",
+ }
+ };
+ const documents = await user.searchDocument(options);
+
+ //then:: files should be sorted properly
+ expect(documents.entities.map(e => e.name)).toEqual(TestHelpers.ALL_FILES.reverse());
+
+ done?.();
+ });
+
+
+
});
diff --git a/tdrive/backend/node/test/e2e/files/files.spec.ts b/tdrive/backend/node/test/e2e/files/files.spec.ts
index 9f00e252..5a6de1ac 100644
--- a/tdrive/backend/node/test/e2e/files/files.spec.ts
+++ b/tdrive/backend/node/test/e2e/files/files.spec.ts
@@ -1,25 +1,24 @@
import "reflect-metadata";
import { afterAll, beforeAll, describe, expect, it } from "@jest/globals";
import { init, TestPlatform } from "../setup";
-import { ResourceUpdateResponse } from "../../../src/utils/types";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import fs from "fs";
-import { File } from "../../../src/services/files/entities/file";
-import { deserialize } from "class-transformer";
-import formAutoContent from "form-auto-content";
import LocalConnectorService from "../../../src/core/platform/services/storage/connectors/local/service";
+import TestHelpers from "../common/common_test_helpers";
describe("The Files feature", () => {
const url = "/internal/services/files/v1";
let platform: TestPlatform;
+ let helpers: TestHelpers;
beforeAll(async () => {
platform = await init({
services: ["webserver", "database", "storage", "files", "previews"],
});
await platform.database.getConnector().init();
+ helpers = await TestHelpers.getInstance(platform)
});
afterAll(async done => {
@@ -28,37 +27,13 @@ describe("The Files feature", () => {
done();
});
- async function uploadFile(file: string) {
- const form = formAutoContent({file: fs.createReadStream(file)});
- form.headers["authorization"] = `Bearer ${await platform.auth.getJWTToken()}`;
-
- const filesUploadRaw = await platform.app.inject({
- method: "POST",
- url: `${url}/companies/${platform.workspace.company_id}/files?thumbnail_sync=1`,
- ...form,
- });
- const filesUpload: ResourceUpdateResponse = deserialize(
- ResourceUpdateResponse,
- filesUploadRaw.body,
- );
- return filesUpload;
- }
-
describe("On user send files", () => {
- const files = [
- "assets/sample.png",
- "assets/sample.gif",
- "assets/sample.pdf",
- "assets/sample.doc",
- "assets/sample.zip",
- "assets/sample.mp4",
- ].map(p => `${__dirname}/${p}`);
const thumbnails = [1, 1, 2, 5, 0, 1];
it("Download file should return 500 if file doesn't exists", async () => {
//given file
- const filesUpload = await uploadFile(files[0]);
- expect(filesUpload.resource.id).toBeTruthy();
+ const filesUpload = await helpers.uploadRandomFile();
+ expect(filesUpload.id).toBeTruthy();
//clean files directory
expect(platform.storage.getConnector()).toBeInstanceOf(LocalConnectorService)
const path = (platform.storage.getConnector()).configuration.path;
@@ -66,7 +41,7 @@ describe("The Files feature", () => {
//when try to download the file
const fileDownloadResponse = await platform.app.inject({
method: "GET",
- url: `${url}/companies/${platform.workspace.company_id}/files/${filesUpload.resource.id}/download`,
+ url: `${url}/companies/${platform.workspace.company_id}/files/${filesUpload.id}/download`,
});
//then file should be not found with 404 error and "File not found message"
expect(fileDownloadResponse).toBeTruthy();
@@ -76,15 +51,15 @@ describe("The Files feature", () => {
it("Download file should return 200 if file exists", async () => {
//given file
- const filesUpload = await uploadFile(files[0]);
- expect(filesUpload.resource.id).toBeTruthy();
+ const filesUpload = await helpers.uploadRandomFile()
+ expect(filesUpload.id).toBeTruthy();
//clean files directory
expect(platform.storage.getConnector()).toBeInstanceOf(LocalConnectorService)
//when try to download the file
const fileDownloadResponse = await platform.app.inject({
method: "GET",
- url: `${url}/companies/${platform.workspace.company_id}/files/${filesUpload.resource.id}/download`,
+ url: `${url}/companies/${platform.workspace.company_id}/files/${filesUpload.id}/download`,
});
//then file should be not found with 404 error and "File not found message"
expect(fileDownloadResponse).toBeTruthy();
@@ -94,20 +69,20 @@ describe("The Files feature", () => {
it.skip("should save file and generate previews", async done => {
- for (const i in files) {
- const file = files[i];
+ for (const i in TestHelpers.ALL_FILES) {
+ const file = TestHelpers.ALL_FILES[i];
- const filesUpload = await uploadFile(file);
+ const filesUpload = await helpers.uploadFile(file);
- expect(filesUpload.resource.id).not.toBeFalsy();
- expect(filesUpload.resource.encryption_key).toBeFalsy(); //This must not be disclosed
- expect(filesUpload.resource.thumbnails.length).toBe(thumbnails[i]);
+ expect(filesUpload.id).not.toBeFalsy();
+ expect(filesUpload.encryption_key).toBeFalsy(); //This must not be disclosed
+ expect(filesUpload.thumbnails.length).toBe(thumbnails[i]);
- for (const thumb of filesUpload.resource.thumbnails) {
+ for (const thumb of filesUpload.thumbnails) {
const thumbnails = await platform.app.inject({
headers: {"authorization": `Bearer ${await platform.auth.getJWTToken()}`},
method: "GET",
- url: `${url}/companies/${platform.workspace.company_id}/files/${filesUpload.resource.id}/thumbnails/${thumb.index}`,
+ url: `${url}/companies/${platform.workspace.company_id}/files/${filesUpload.id}/thumbnails/${thumb.index}`,
});
expect(thumbnails.statusCode).toBe(200);
}
diff --git a/tdrive/backend/node/test/e2e/setup/index.ts b/tdrive/backend/node/test/e2e/setup/index.ts
index 6e5e94da..44273491 100644
--- a/tdrive/backend/node/test/e2e/setup/index.ts
+++ b/tdrive/backend/node/test/e2e/setup/index.ts
@@ -13,6 +13,7 @@ import config from "config";
import globalResolver from "../../../src/services/global-resolver";
import {FileServiceImpl} from "../../../src/services/files/services";
import StorageAPI from "../../../src/core/platform/services/storage/provider";
+import {SearchServiceAPI} from "../../../src/core/platform/services/search/api";
type TokenPayload = {
sub: string;
@@ -23,7 +24,7 @@ type TokenPayload = {
};
};
-type User = {
+export type User = {
id: string;
isWorkspaceModerator?: boolean;
};
@@ -42,6 +43,7 @@ export interface TestPlatform {
getJWTToken(payload?: TokenPayload): Promise;
};
tearDown(): Promise;
+ search: SearchServiceAPI;
}
export interface TestPlatformConfiguration {
@@ -76,6 +78,7 @@ export async function init(
const messageQueue = platform.getProvider("message-queue");
const auth = platform.getProvider("auth");
const storage: StorageAPI = platform.getProvider("storage");
+ const search: SearchServiceAPI = platform.getProvider("search");
testPlatform = {
platform,
@@ -91,6 +94,7 @@ export async function init(
getJWTToken,
},
tearDown,
+ search,
};
}
@@ -112,7 +116,7 @@ export async function init(
payload.sub = testPlatform.currentUser.id;
}
- if (testPlatform .currentUser.isWorkspaceModerator) {
+ if (testPlatform.currentUser.isWorkspaceModerator) {
payload.org = {};
payload.org[testPlatform.workspace.company_id] = {
role: "",
diff --git a/tdrive/backend/node/test/e2e/setup/default.test.json b/tdrive/backend/node/test/e2e/setup/test.json
similarity index 74%
rename from tdrive/backend/node/test/e2e/setup/default.test.json
rename to tdrive/backend/node/test/e2e/setup/test.json
index ec387742..c12ad975 100644
--- a/tdrive/backend/node/test/e2e/setup/default.test.json
+++ b/tdrive/backend/node/test/e2e/setup/test.json
@@ -6,6 +6,12 @@
"level": "warn"
}
},
+ "search": {
+ "type": "elasticsearch",
+ "elasticsearch": {
+ "endpoint": "http://localhost:9200"
+ }
+ },
"websocket": {
"path": "/socket",
"adapters": {
diff --git a/tdrive/backend/node/test/http/badges.http b/tdrive/backend/node/test/http/badges.http
deleted file mode 100644
index 932df6ea..00000000
--- a/tdrive/backend/node/test/http/badges.http
+++ /dev/null
@@ -1,19 +0,0 @@
-@company_id = bcfe2f79-8e81-42a3-b551-3a32d49b2b4c
-@workspace_id = 3328552c-5ccd-4172-b84a-d876d56aa71b
-@user_id = 3328552c-5ccd-4172-b84a-d876d56aa71c
-
-@baseURL = http://localhost:3000
-@badgesURL = {{baseURL}}/internal/services/notifications/v1/badges
-
-# @name login
-GET {{baseURL}}/api/auth/login
-
-@authToken = {{login.response.body.token}}
-@currentUserId = {{login.response.body.user.id}}
-
-### List badges with all websockets
-
-GET {{badgesURL}}/?company_id={{company_id}}&websockets=true&limit=5
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
diff --git a/tdrive/backend/node/test/http/channel-as-read.http b/tdrive/backend/node/test/http/channel-as-read.http
deleted file mode 100644
index 2ee7cdc0..00000000
--- a/tdrive/backend/node/test/http/channel-as-read.http
+++ /dev/null
@@ -1,61 +0,0 @@
-@company_id = bcfe2f79-8e81-42a3-b551-3a32d49b2b4c
-@workspace_id = 3328552c-5ccd-4172-b84a-d876d56aa71b
-@user_id = 3328552c-5ccd-4172-b84a-d876d56aa71c
-
-@baseURL = http://localhost:3000
-@channelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/{{workspace_id}}
-@directChannelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/direct
-
-# @name login
-GET {{baseURL}}/api/auth/login
-
-@authToken = {{login.response.body.token}}
-@currentUserId = {{login.response.body.user.id}}
-
-### Create a channel
-
-# @name createChannel
-POST {{channelsURL}}/channels
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "name": "My channel",
- "icon": "tdrive logo",
- "description": "This channel allow tdrive's team to chat easily",
- "channel_group": "tdrive",
- "visibility": "public",
- "is_default": true,
- "archived": false
- }
-}
-
-### Get a single channel
-
-@getId = {{createChannel.response.body.resource.id}}
-
-GET {{channelsURL}}/channels/{{getId}}
-Authorization: Bearer {{authToken}}
-
-### Add current user as member to a channel (join channel)
-
-POST {{channelsURL}}/channels/{{getId}}/members
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "user_id": "{{currentUserId}}"
- }
-}
-
-### Mark the channel as read/unread
-
-POST {{channelsURL}}/channels/{{getId}}/read
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "value": true
-}
diff --git a/tdrive/backend/node/test/http/channels.http b/tdrive/backend/node/test/http/channels.http
deleted file mode 100644
index c665d84c..00000000
--- a/tdrive/backend/node/test/http/channels.http
+++ /dev/null
@@ -1,133 +0,0 @@
-@company_id = bcfe2f79-8e81-42a3-b551-3a32d49b2b4c
-@workspace_id = 3328552c-5ccd-4172-b84a-d876d56aa71b
-@user_id = 3328552c-5ccd-4172-b84a-d876d56aa71c
-
-@baseURL = http://localhost:3000
-@channelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/{{workspace_id}}
-@directChannelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/direct
-
-# @name login
-GET {{baseURL}}/api/auth/login
-
-@authToken = {{login.response.body.token}}
-@currentUserId = {{login.response.body.user.id}}
-
-### List workspace channels with all websockets
-
-GET {{channelsURL}}/channels?websockets=true&limit=5
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-### List user channels with all websockets
-
-@authToken = {{login.response.body.token}}
-GET {{channelsURL}}/channels?websockets=true&mine=true&limit=5
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-### Create a channel
-
-# @name createChannel
-POST {{channelsURL}}/channels
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "name": "My channel",
- "icon": "tdrive logo",
- "description": "This channel allow tdrive's team to chat easily",
- "channel_group": "tdrive",
- "visibility": "public",
- "is_default": true,
- "archived": false
- }
-}
-
-### Get a single channel
-
-@getId = {{createChannel.response.body.resource.id}}
-
-GET {{channelsURL}}/channels/{{getId}}
-Authorization: Bearer {{authToken}}
-
-### Update a channel
-@updateId = {{createChannel.response.body.resource.id}}
-
-POST {{channelsURL}}/channels/{{updateId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "name": "My channel",
- "description": "Hello world",
- "is_default": false
- }
-}
-
-### Delete a channel
-
-@deleteId = {{createChannel.response.body.resource.id}}
-
-DELETE {{channelsURL}}/channels/{{deleteId}}
-Authorization: Bearer {{authToken}}
-
-
-### Get all channel members
-
-GET {{channelsURL}}/channels/{{getId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-
-### Add current user as member to a channel (join channel)
-
-POST {{channelsURL}}/channels/{{getId}}/members
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "user_id": "{{currentUserId}}"
- }
-}
-
-### Get a channel member
-
-GET {{channelsURL}}/channels/{{getId}}/members/{{currentUserId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-### Update current channel member
-
-POST {{channelsURL}}/channels/{{getId}}/members/{{currentUserId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "favorite": true,
- "notification_level": "none",
- "hello": 1
- }
-}
-
-### Current user quits the channel
-
-DELETE {{channelsURL}}/channels/{{getId}}/members/{{currentUserId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-### Errors Tests
-
-### Call without the JWT token should HTTP 401
-
-GET {{channelsURL}}/channels
-Content-Type: application/json
-
-### Get a channel which may not exists
-
-GET {{channelsURL}}/channels/0b0e1492-f596-46b9-a4fb-c12d71b2696e
-Authorization: Bearer {{authToken}}
-
diff --git a/tdrive/backend/node/test/http/direct-channels.http b/tdrive/backend/node/test/http/direct-channels.http
deleted file mode 100644
index c3bbe15d..00000000
--- a/tdrive/backend/node/test/http/direct-channels.http
+++ /dev/null
@@ -1,163 +0,0 @@
-@company_id = bcfe2f79-8e81-42a3-b551-3a32d49b2b4c
-@workspace_id = 3328552c-5ccd-4172-b84a-d876d56aa71b
-
-@baseURL = http://localhost:3000
-@channelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/{{workspace_id}}
-@directChannelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/direct
-
-### Login as user 1
-
-# @name login
-GET {{baseURL}}/api/auth/login
-
-@authToken1 = {{login.response.body.token}}
-@userId1 = {{login.response.body.user.id}}
-
-### Login as user 2
-
-# @name login2
-
-GET {{baseURL}}/api/auth/login
-
-@authToken2 = {{login2.response.body.token}}
-@userId2 = {{login2.response.body.user.id}}
-
-### Login as user 3
-
-# @name login3
-
-GET {{baseURL}}/api/auth/login
-
-@authToken3 = {{login3.response.body.token}}
-@userId3 = {{login3.response.body.user.id}}
-
-### Create a direct channel
-
-# @name createDirectChannel
-POST {{directChannelsURL}}/channels
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-{
- "options": {
- "members": ["{{userId2}}"]
- },
- "resource": {
- "icon": "hello",
- "description": "A direct channel",
- "channel_group": "tdrive",
- "is_default": false,
- "archived": false
- }
-}
-
-### Direct channel details as user 1 should work
-@directId = {{createDirectChannel.response.body.resource.id}}
-
-GET {{directChannelsURL}}/channels/{{directId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-### Direct channel details as user 2 (member) should work
-
-GET {{directChannelsURL}}/channels/{{directId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken2}}
-
-### Direct channel details as user 3 (not member) should not work
-
-GET {{directChannelsURL}}/channels/{{directId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken3}}
-
-### Direct channel members
-
-GET {{directChannelsURL}}/channels/{{directId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-### List direct channels for user in company
-
-@authToken = {{login.response.body.token}}
-GET {{directChannelsURL}}/channels?websockets=true&limit=5
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-### Update the direct channel description
-
-POST {{directChannelsURL}}/channels/{{directId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-{
- "resource": {
- "description": "A new direct channel description"
- }
-}
-
-### Update the direct channel name will do nothing since the name is useless
-POST {{directChannelsURL}}/channels/{{directId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-{
- "resource": {
- "name": "A new direct channel name"
- }
-}
-
-### Update the direct channel member settings for current user
-
-POST {{directChannelsURL}}/channels/{{directId}}/members/{{userId1}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-{
- "resource": {
- "favorite": true,
- "notification_level": "all"
- }
-}
-
-### Update another user member settings should fail
-
-POST {{directChannelsURL}}/channels/{{directId}}/members/{{userId2}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-{
- "resource": {
- "favorite": true,
- "notification_level": "none"
- }
-}
-
-### Get member settings as user1
-
-GET {{directChannelsURL}}/channels/{{directId}}/members/{{userId1}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-### Get member settings as user2
-
-GET {{directChannelsURL}}/channels/{{directId}}/members/{{userId2}}
-Content-Type: application/json
-Authorization: Bearer {{authToken2}}
-
-### Get other member settings should fail
-
-GET {{directChannelsURL}}/channels/{{directId}}/members/{{userId2}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-### Leave a direct channel of another user should fail
-
-DELETE {{directChannelsURL}}/channels/{{directId}}/members/{{userId2}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
-
-### Leave a direct channel should not fail
-
-DELETE {{directChannelsURL}}/channels/{{directId}}/members/{{userId1}}
-Content-Type: application/json
-Authorization: Bearer {{authToken1}}
diff --git a/tdrive/backend/node/test/http/elasticsearch.http b/tdrive/backend/node/test/http/elasticsearch.http
new file mode 100644
index 00000000..bcc1eae8
--- /dev/null
+++ b/tdrive/backend/node/test/http/elasticsearch.http
@@ -0,0 +1,251 @@
+# The task is:
+# to find all the files that I havee access to, it shoud be a doc files,
+# that was modified during the last week and share with my by Diana Potoking
+
+
+### Drop index
+DELETE https://localhost:9200/tdrive_files_extended
+
+### Delete Index
+DELETE https://localhost:9200/drive_files
+
+### Create index
+PUT https://localhost:9200/tdrive_files_extended
+Content-Type: application/json
+
+{
+ "drive_files": {
+ "aliases": {},
+ "mappings": {
+ "_source": {
+ "enabled": true
+ }
+ }
+ }
+}
+
+
+
+### Check that it is created
+GET https://localhost:9200/_cat/indices
+
+### GET index info
+GET https://localhost:9200/drive_files
+
+### GET data to the index
+GET https://localhost:9200/drive_files/_doc/8c03c5a1-0146-11ee-82d9-f503f8a58e9d
+Content-Type: application/json
+
+### Put data to the index
+PUT https://localhost:9200/tdrive_files_extended/_doc/0001
+Content-Type: application/json
+
+{
+ "file_name": "file0001.txt",
+ "access_info": [
+ {
+ "type": "user",
+ "id": "user01",
+ "level": "read",
+ "grantor": "user02"
+ }
+ ]
+}
+
+###
+PUT https://localhost:9200/tdrive_files_extended/_doc/8c03c5a1-0146-11ee-82d9-f503f8a58e9d
+Content-Type: application/json
+
+{
+ "file_name": "file0002.png",
+ "access_info": [
+ {
+ "type": "user",
+ "id": "user01",
+ "level": "read",
+ "grantor": "user03"
+ }
+ ]
+}
+
+### Search all in the index by last_modified
+GET https://localhost:9200/drive_files/_search
+Content-Type: application/json
+
+{
+ "query": {
+
+ "bool": {
+ "boost": 1,
+ "must": [
+ {
+ "range":
+ {
+ "last_modified": {
+ "gte": "0"
+ }
+ }
+ },
+ {
+ "range":
+ {
+ "last_modified": {
+ "lte": "1685958426348"
+ }
+ }
+ },
+ {
+ "bool": {
+ "should": [
+ {
+ "match": {
+ "access_entities": {
+ "query": "edfb30e0-0385-11ee-80e8-41892804174d",
+ "operator": "AND"
+ }
+ }
+ },
+ {
+ "match": {
+ "access_entities": {
+ "query": "edd447f1-0385-11ee-80e8-41892804174d",
+ "operator": "AND"
+ }
+ }
+ }
+ ],
+ "minimum_should_match": 1
+ }
+ },
+ {
+ "bool": {
+ "should": [
+ {
+ "match": {
+ "company_id": {
+ "query": "edd447f1-0385-11ee-80e8-41892804174d",
+ "operator": "AND"
+ }
+ }
+ }
+ ],
+ "minimum_should_match": 1
+ }
+ }
+ ]
+ }
+ }
+}
+
+
+### Search all in the index and order by name
+GET https://localhost:9200/drive_files/_search
+Content-Type: application/json
+
+{
+ "query": {
+ "bool": {
+ "boost": 1,
+ "must": [
+ {
+ "bool": {
+ "should": [
+ {
+ "match": {
+ "access_entities": {
+ "query": "12fb6a40-03b3-11ee-af31-a569970b7f74",
+ "operator": "AND"
+ }
+ }
+ },
+ {
+ "match": {
+ "access_entities": {
+ "query": "12f290a1-03b3-11ee-af31-a569970b7f74",
+ "operator": "AND"
+ }
+ }
+ }
+ ],
+ "minimum_should_match": 1
+ }
+ },
+ {
+ "bool": {
+ "should": [
+ {
+ "match": {
+ "company_id": {
+ "query": "12f290a1-03b3-11ee-af31-a569970b7f74",
+ "operator": "AND"
+ }
+ }
+ }
+ ],
+ "minimum_should_match": 1
+ }
+ }
+ ]
+ }
+ },
+ "sort": [
+ {
+ "name": "asc"
+ }
+ ]
+}
+
+### Search all in the index and order by name
+GET https://localhost:9200/drive_files/_search
+Content-Type: application/json
+
+{
+ "query": {
+ "bool": {
+ "boost": 1,
+ "must": [
+ {
+ "bool": {
+ "should": [
+ {
+ "match": {
+ "access_entities": {
+ "query": "4090dce0-03ba-11ee-97d7-fb8fa15d86a1",
+ "operator": "AND"
+ }
+ }
+ },
+ {
+ "match": {
+ "access_entities": {
+ "query": "40808931-03ba-11ee-97d7-fb8fa15d86a1",
+ "operator": "AND"
+ }
+ }
+ }
+ ],
+ "minimum_should_match": 1
+ }
+ },
+ {
+ "bool": {
+ "should": [
+ {
+ "match": {
+ "company_id": {
+ "query": "40808931-03ba-11ee-97d7-fb8fa15d86a1",
+ "operator": "AND"
+ }
+ }
+ }
+ ],
+ "minimum_should_match": 1
+ }
+ }
+ ]
+ }
+ }
+}
+
+
+
diff --git a/tdrive/backend/node/test/http/private-channels-membership.http b/tdrive/backend/node/test/http/private-channels-membership.http
deleted file mode 100644
index 5b1fc3f1..00000000
--- a/tdrive/backend/node/test/http/private-channels-membership.http
+++ /dev/null
@@ -1,108 +0,0 @@
-@company_id = bcfe2f79-8e81-42a3-b551-3a32d49b2b4c
-@workspace_id = 3328552c-5ccd-4172-b84a-d876d56aa71b
-@baseURL = http://localhost:3000
-@channelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/{{workspace_id}}
-@directChannelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/direct
-
-### Login as user 1
-
-# @name login
-GET {{baseURL}}/api/auth/login
-
-@authTokenUser1 = {{login.response.body.token}}
-@currentUserId1 = {{login.response.body.user.id}}
-
-### Login as user 2
-
-# @name login2
-GET {{baseURL}}/api/auth/login
-
-@authTokenUser2 = {{login2.response.body.token}}
-@currentUserId2 = {{login2.response.body.user.id}}
-
-### User 1 creates a private channel
-
-# @name createChannel
-POST {{channelsURL}}/channels
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-{
- "resource": {
- "name": "My private channel",
- "icon": "tdrive logo",
- "description": "This channel allow tdrive's team to chat easily",
- "channel_group": "tdrive",
- "visibility": "private",
- "is_default": true,
- "archived": false
- }
-}
-
-### Get the private channel
-
-@getId = {{createChannel.response.body.resource.id}}
-
-GET {{channelsURL}}/channels/{{getId}}
-Authorization: Bearer {{authTokenUser1}}
-
-### Get all channel members
-
-GET {{channelsURL}}/channels/{{getId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-
-### The user 2 tries to join the private channel, this should reject
-
-POST {{channelsURL}}/channels/{{getId}}/members
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
-
-{
- "resource": {
- "user_id": "{{currentUserId2}}"
- }
-}
-
-### The user 1 adds the user 2 as channel member, this should be OK
-
-POST {{channelsURL}}/channels/{{getId}}/members
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-{
- "resource": {
- "user_id": "{{currentUserId2}}"
- }
-}
-
-### User 2 lists his channels
-
-GET {{channelsURL}}/channels?websockets=true&mine=true&limit=5
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
-
-### Get all channel members as user 1
-
-GET {{channelsURL}}/channels/{{getId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-### Get all channel members as user 2
-
-GET {{channelsURL}}/channels/{{getId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
-
-### User 1 leaves the channel he created
-
-DELETE {{channelsURL}}/channels/{{getId}}/members/{{currentUserId1}}
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-### User 2 leaves the channel: Error since he is the last member, he can not leave
-
-DELETE {{channelsURL}}/channels/{{getId}}/members/{{currentUserId2}}
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
diff --git a/tdrive/backend/node/test/http/private-channels.http b/tdrive/backend/node/test/http/private-channels.http
deleted file mode 100644
index cbfecc4a..00000000
--- a/tdrive/backend/node/test/http/private-channels.http
+++ /dev/null
@@ -1,70 +0,0 @@
-@company_id = bcfe2f79-8e81-42a3-b551-3a32d49b2b4c
-@workspace_id = 3328552c-5ccd-4172-b84a-d876d56aa71b
-@user_id = 3328552c-5ccd-4172-b84a-d876d56aa71c
-
-@baseURL = http://localhost:3000
-@channelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/{{workspace_id}}
-@directChannelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/direct
-
-# @name login
-GET {{baseURL}}/api/auth/login
-
-@authToken = {{login.response.body.token}}
-@currentUserId = {{login.response.body.user.id}}
-
-### List workspace channels with all websockets
-
-GET {{channelsURL}}/channels?websockets=true&limit=5
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-### List user channels with all websockets
-
-@authToken = {{login.response.body.token}}
-GET {{channelsURL}}/channels?websockets=true&mine=true&limit=5
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-### Create a private channel
-
-# @name createChannel
-POST {{channelsURL}}/channels
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "name": "My private channel",
- "icon": "tdrive logo",
- "description": "A private channel",
- "channel_group": "tdrive",
- "visibility": "private",
- "is_default": true,
- "archived": false
- }
-}
-
-### Get the private channel
-
-@getId = {{createChannel.response.body.resource.id}}
-
-GET {{channelsURL}}/channels/{{getId}}
-Authorization: Bearer {{authToken}}
-
-### Get members of the private channel
-
-GET {{channelsURL}}/channels/{{getId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-### Get current user as member
-
-GET {{channelsURL}}/channels/{{getId}}/members/{{currentUserId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-### Current user quits the private channel will fail because he is the only one in it
-
-DELETE {{channelsURL}}/channels/{{getId}}/members/{{currentUserId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
diff --git a/tdrive/backend/node/test/http/public-channels-membership.http b/tdrive/backend/node/test/http/public-channels-membership.http
deleted file mode 100644
index 04123337..00000000
--- a/tdrive/backend/node/test/http/public-channels-membership.http
+++ /dev/null
@@ -1,108 +0,0 @@
-@company_id = bcfe2f79-8e81-42a3-b551-3a32d49b2b4c
-@workspace_id = 3328552c-5ccd-4172-b84a-d876d56aa71b
-@baseURL = http://localhost:3000
-@channelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/{{workspace_id}}
-@directChannelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/direct
-
-### Login as user 1
-
-# @name login
-GET {{baseURL}}/api/auth/login
-
-@authTokenUser1 = {{login.response.body.token}}
-@currentUserId1 = {{login.response.body.user.id}}
-
-### Login as user 2
-
-# @name login2
-GET {{baseURL}}/api/auth/login
-
-@authTokenUser2 = {{login2.response.body.token}}
-@currentUserId2 = {{login2.response.body.user.id}}
-
-### User 1 creates a public channel
-
-# @name createChannel
-POST {{channelsURL}}/channels
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-{
- "resource": {
- "name": "A public channel",
- "icon": "tdrive logo",
- "description": "This channel allow tdrive's team to chat easily",
- "channel_group": "tdrive",
- "visibility": "public",
- "is_default": true,
- "archived": false
- }
-}
-
-### Get the public channel
-
-@getId = {{createChannel.response.body.resource.id}}
-
-GET {{channelsURL}}/channels/{{getId}}
-Authorization: Bearer {{authTokenUser1}}
-
-### Get all channel members
-
-GET {{channelsURL}}/channels/{{getId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-
-### The user 2 joins the public channel, this should be OK
-
-POST {{channelsURL}}/channels/{{getId}}/members
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
-
-{
- "resource": {
- "user_id": "{{currentUserId2}}"
- }
-}
-
-### Get all channel members, there are now 2 members
-
-GET {{channelsURL}}/channels/{{getId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-### User 1 lists his channels
-
-GET {{channelsURL}}/channels?mine=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-### User 2 lists his channels
-
-GET {{channelsURL}}/channels?mine=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
-
-### User 2 leaves the channel
-
-DELETE {{channelsURL}}/channels/{{getId}}/members/{{currentUserId2}}
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
-
-### Get all channel members, there are now 1 member, the initial one
-
-GET {{channelsURL}}/channels/{{getId}}/members?websockets=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser1}}
-
-### List channels as user 2, current channel appears since it is public
-
-GET {{channelsURL}}/channels
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
-
-### User 2 list his public channels, current channel does not appear since he left it
-
-GET {{channelsURL}}/channels?mine=true
-Content-Type: application/json
-Authorization: Bearer {{authTokenUser2}}
\ No newline at end of file
diff --git a/tdrive/backend/node/test/http/tabs.http b/tdrive/backend/node/test/http/tabs.http
deleted file mode 100644
index fcd432b4..00000000
--- a/tdrive/backend/node/test/http/tabs.http
+++ /dev/null
@@ -1,85 +0,0 @@
-@company_id = bcfe2f79-8e81-42a3-b551-3a32d49b2b4c
-@workspace_id = 3328552c-5ccd-4172-b84a-d876d56aa71a
-
-
-@baseURL = http://localhost:3000
-@tabsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/{{workspace_id}}/channels
-@channelsURL = {{baseURL}}/internal/services/channels/v1/companies/{{company_id}}/workspaces/{{workspace_id}}
-
-
-# @name login
-GET {{baseURL}}/api/auth/login
-
-@authToken = {{login.response.body.token}}
-@currentUserId = {{login.response.body.user.id}}
-
-
-### List channel's tab with all websockets
-GET {{tabsURL}}/{{channelId}}/tabs?websockets=true&limit=5
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-
-### Create a channel
-# @name createChannel
-POST {{channelsURL}}/channels
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "name": "My channel",
- "icon": "tdrive logo",
- "description": "This channel allow tdrive's team to chat easily",
- "channel_group": "tdrive",
- "visibility": "public",
- "is_default": true,
- "archived": false
- }
-}
-
-
-### Create a tab
-# @name createTab
-@channelId = {{createChannel.response.body.resource.id}}
-
-POST {{tabsURL}}/{{channelId}}/tabs
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- // WARNING : special caracter in tab's name does not work
- "name": "My tabs name",
- "configuration": "JSON"
- }
-}
-
-
-### Get a single tab
-@getId = {{createTab.response.body.resource.id}}
-
-GET {{tabsURL}}/{{channelId}}/tabs/{{getId}}
-Authorization: Bearer {{authToken}}
-
-
-### Update a tab
-@updateId = {{createTab.response.body.resource.id}}
-
-POST {{tabsURL}}/{{channelId}}/tabs/{{updateId}}
-Content-Type: application/json
-Authorization: Bearer {{authToken}}
-
-{
- "resource": {
- "name": "My tab updated",
- "configuration": "JSON"
- }
-}
-
-
-### Delete a tab
-@deleteId = {{createTab.response.body.resource.id}}
-
-DELETE {{tabsURL}}/{{channelId}}/tabs/{{deleteId}}
-Authorization: Bearer {{authToken}}
\ No newline at end of file
diff --git a/tdrive/backend/node/test/unit/core/services/search/adapters/elasticsearch/search.test.ts b/tdrive/backend/node/test/unit/core/services/search/adapters/elasticsearch/search.test.ts
new file mode 100644
index 00000000..b025e2c3
--- /dev/null
+++ b/tdrive/backend/node/test/unit/core/services/search/adapters/elasticsearch/search.test.ts
@@ -0,0 +1,158 @@
+import "reflect-metadata";
+import {describe, it} from "@jest/globals";
+import {buildSearchQuery} from "../../../../../../../src/core/platform/services/search/adapters/elasticsearch/search";
+import {DriveFile} from "../../../../../../../src/services/documents/entities/drive-file";
+import {FindFilter, FindOptions} from "../../../../../../../src/core/platform/services/search/api";
+import {
+ comparisonType
+} from "../../../../../../../src/core/platform/services/database/services/orm/repository/repository";
+
+describe("ES Query Builder", () => {
+
+ describe("The intervals section of the search query", () => {
+
+ it("'lte' section become a proper 'range' array in ES query", async () => {
+ //given::
+ const filters: FindFilter = {};
+ const options: FindOptions = {
+ $lte: [["last_modified", 10] as comparisonType]
+ };
+
+ //when
+ let esReq = buildSearchQuery(DriveFile, filters, options);
+
+ //then
+ const expected: any = {
+ bool: {
+ boost: 1,
+ must: [
+ {
+ range: {
+ last_modified: {
+ lte: 10
+ }
+ }
+ }
+ ]
+ }
+ }
+ const query = esReq.esParams.body["query"];
+ expect(query).not.toBeNull();
+ expect(query).toEqual(expected);
+ });
+
+ it("'lte' section shouldn't exist if some params are missing", async () => {
+ //given::
+ const filters: FindFilter = {};
+ const options: FindOptions = {
+ $lte: []
+ };
+
+ //when
+ let esReq = buildSearchQuery(DriveFile, filters, options);
+
+ //then
+ const expected: any = {
+ bool: {
+ boost: 1
+ }
+ }
+ const query = esReq.esParams.body["query"];
+ expect(query).not.toBeNull();
+ expect(query).toEqual(expected);
+ });
+
+ it("'gte' section become a proper 'range' array in ES query", async () => {
+ //given::
+ const filters: FindFilter = {};
+ const options: FindOptions = {
+ $gte: [["last_modified", 10] as comparisonType]
+ };
+
+ //when
+ let esReq = buildSearchQuery(DriveFile, filters, options);
+
+ //then
+ const expected: any = {
+ bool: {
+ boost: 1,
+ must: [
+ {
+ range: {
+ last_modified: {
+ gte: 10
+ }
+ }
+ }
+ ]
+ }
+ }
+ const query = esReq.esParams.body["query"];
+ expect(query).not.toBeNull();
+ expect(query).toEqual(expected);
+ });
+
+ it("'gt' section become a proper 'range' array in ES query", async () => {
+ //given::
+ const filters: FindFilter = {};
+ const options: FindOptions = {
+ $gt: [["last_modified", 10] as comparisonType]
+ };
+
+ //when
+ let esReq = buildSearchQuery(DriveFile, filters, options);
+
+ //then
+ const expected: any = {
+ bool: {
+ boost: 1,
+ must: [
+ {
+ range: {
+ last_modified: {
+ gt: 10
+ }
+ }
+ }
+ ]
+ }
+ }
+ const query = esReq.esParams.body["query"];
+ expect(query).not.toBeNull();
+ expect(query).toEqual(expected);
+ });
+
+ it("'lt' section become a proper 'range' array in ES query", async () => {
+ //given::
+ const filters: FindFilter = {};
+ const options: FindOptions = {
+ $lt: [["last_modified", 10] as comparisonType]
+ };
+
+ //when
+ let esReq = buildSearchQuery(DriveFile, filters, options);
+
+ //then
+ const expected: any = {
+ bool: {
+ boost: 1,
+ must: [
+ {
+ range: {
+ last_modified: {
+ lt: 10
+ }
+ }
+ }
+ ]
+ }
+ }
+ const query = esReq.esParams.body["query"];
+ expect(query).not.toBeNull();
+ expect(query).toEqual(expected);
+ });
+
+ });
+
+
+});
\ No newline at end of file