🐛 Not all the data is shown in the shared with me section (#376)
* 🐛Not all the data is shown in the shared with me section
This commit is contained in:
+8
-2
@@ -283,11 +283,12 @@ export default class ESAndOpenSearch extends SearchAdapter implements SearchAdap
|
|||||||
const esParamsWithScroll = {
|
const esParamsWithScroll = {
|
||||||
...esParams,
|
...esParams,
|
||||||
size: parseInt(options.pagination.limitStr || "100"),
|
size: parseInt(options.pagination.limitStr || "100"),
|
||||||
scroll: "1m",
|
scroll: "10m",
|
||||||
};
|
};
|
||||||
|
|
||||||
let esResponse: any;
|
let esResponse: any;
|
||||||
|
|
||||||
|
let nextToken;
|
||||||
if (options.pagination.page_token) {
|
if (options.pagination.page_token) {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -297,18 +298,23 @@ export default class ESAndOpenSearch extends SearchAdapter implements SearchAdap
|
|||||||
},
|
},
|
||||||
esOptions,
|
esOptions,
|
||||||
);
|
);
|
||||||
|
//the scroll token is also the same, and we do not get it from response
|
||||||
|
nextToken = options.pagination.page_token;
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
esResponse = await this.client.search(esParamsWithScroll, esOptions);
|
esResponse = await this.client.search(esParamsWithScroll, esOptions);
|
||||||
|
nextToken = esResponse.body?._scroll_id || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esResponse.statusCode !== 200) {
|
if (esResponse.statusCode !== 200) {
|
||||||
logger.error(`${this.name} - ${JSON.stringify(esResponse.body)}`);
|
logger.error(`${this.name} - ${JSON.stringify(esResponse.body)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextToken = esResponse.body?._scroll_id || "";
|
|
||||||
const hits = esResponse.body?.hits?.hits || [];
|
const hits = esResponse.body?.hits?.hits || [];
|
||||||
|
if (hits.length == 0) {
|
||||||
|
nextToken = false;
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug(`${this.name} got response: ${JSON.stringify(esResponse)}`);
|
logger.debug(`${this.name} got response: ${JSON.stringify(esResponse)}`);
|
||||||
|
|
||||||
|
|||||||
@@ -200,9 +200,10 @@ export default class MongoSearch extends SearchAdapter implements SearchAdapterI
|
|||||||
|
|
||||||
const { query, sort, project } = buildSearchQuery<EntityType>(entityType, filters, options);
|
const { query, sort, project } = buildSearchQuery<EntityType>(entityType, filters, options);
|
||||||
|
|
||||||
|
logger.info(`Search query: ${JSON.stringify(query)}`);
|
||||||
console.log(query);
|
console.log(query);
|
||||||
|
|
||||||
let cursor = collection.find({ ...query }).sort(sort);
|
let cursor = collection.find(query).sort(sort);
|
||||||
if (project) {
|
if (project) {
|
||||||
cursor = cursor.project(project);
|
cursor = cursor.project(project);
|
||||||
}
|
}
|
||||||
@@ -230,7 +231,7 @@ export default class MongoSearch extends SearchAdapter implements SearchAdapterI
|
|||||||
|
|
||||||
const nextToken =
|
const nextToken =
|
||||||
entities.length === parseInt(options.pagination.limitStr) &&
|
entities.length === parseInt(options.pagination.limitStr) &&
|
||||||
(parseInt(options.pagination.page_token) + 1).toString(10);
|
(parseInt(options.pagination.page_token || "0") + 1).toString(10);
|
||||||
const nextPage: Paginable = new Pagination(nextToken, options.pagination.limitStr || "100");
|
const nextPage: Paginable = new Pagination(nextToken, options.pagination.limitStr || "100");
|
||||||
|
|
||||||
logger.info(`Found ${entities.length} results on entity ${searchPrefix}${index}`);
|
logger.info(`Found ${entities.length} results on entity ${searchPrefix}${index}`);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default class SearchRepository<EntityType> {
|
|||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
//2. Get database original objects from theses primary keys
|
//2. Get database original objects from these primary keys
|
||||||
for (const searchEntity of searchResults.getEntities().sort((a, b) => b.score - a.score)) {
|
for (const searchEntity of searchResults.getEntities().sort((a, b) => b.score - a.score)) {
|
||||||
const sourceEntity = await repository.findOne(searchEntity.primaryKey, {}, context);
|
const sourceEntity = await repository.findOne(searchEntity.primaryKey, {}, context);
|
||||||
if (sourceEntity) {
|
if (sourceEntity) {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import SearchRepository from "../../../core/platform/services/search/repository";
|
import SearchRepository from "../../../core/platform/services/search/repository";
|
||||||
import { getLogger, logger, TdriveLogger } from "../../../core/platform/framework";
|
import { getLogger, logger, TdriveLogger } from "../../../core/platform/framework";
|
||||||
import { CrudException, ListResult } from "../../../core/platform/framework/api/crud-service";
|
import {
|
||||||
|
CrudException,
|
||||||
|
ListResult,
|
||||||
|
Pagination,
|
||||||
|
} from "../../../core/platform/framework/api/crud-service";
|
||||||
import Repository, {
|
import Repository, {
|
||||||
comparisonType,
|
comparisonType,
|
||||||
inType,
|
inType,
|
||||||
@@ -96,13 +100,7 @@ export class DocumentsService {
|
|||||||
context: DriveExecutionContext & { public_token?: string },
|
context: DriveExecutionContext & { public_token?: string },
|
||||||
): Promise<BrowseDetails> => {
|
): Promise<BrowseDetails> => {
|
||||||
if (isSharedWithMeFolder(id)) {
|
if (isSharedWithMeFolder(id)) {
|
||||||
const children = await this.search(options, context);
|
return this.sharedWithMe(options, context);
|
||||||
return {
|
|
||||||
access: "read",
|
|
||||||
children: children.getEntities(),
|
|
||||||
nextPage: children.nextPage,
|
|
||||||
path: [] as Array<DriveFile>,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
nextPage: null,
|
nextPage: null,
|
||||||
@@ -111,6 +109,25 @@ export class DocumentsService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sharedWithMe = async (
|
||||||
|
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);
|
||||||
|
return {
|
||||||
|
access: "read",
|
||||||
|
children: result,
|
||||||
|
nextPage: null,
|
||||||
|
path: [] as Array<DriveFile>,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
userQuota = async (context: CompanyExecutionContext): Promise<number> => {
|
userQuota = async (context: CompanyExecutionContext): Promise<number> => {
|
||||||
const children = await this.repository.find({
|
const children = await this.repository.find({
|
||||||
parent_id: "user_" + context.user.id,
|
parent_id: "user_" + context.user.id,
|
||||||
@@ -955,9 +972,9 @@ export class DocumentsService {
|
|||||||
const result = await this.searchRepository.search(
|
const result = await this.searchRepository.search(
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
pagination: {
|
pagination: options.pagination
|
||||||
limitStr: "100",
|
? Pagination.fromPaginable(options.pagination)
|
||||||
},
|
: new Pagination(),
|
||||||
$in: [
|
$in: [
|
||||||
...(options.onlyDirectlyShared ? [["access_entities", [context.user.id]] as inType] : []),
|
...(options.onlyDirectlyShared ? [["access_entities", [context.user.id]] as inType] : []),
|
||||||
...(options.company_id ? [["company_id", [options.company_id]] as inType] : []),
|
...(options.company_id ? [["company_id", [options.company_id]] as inType] : []),
|
||||||
@@ -999,7 +1016,7 @@ export class DocumentsService {
|
|||||||
if (!options.onlyDirectlyShared) {
|
if (!options.onlyDirectlyShared) {
|
||||||
const filteredResult = await this.filter(result.getEntities(), async item => {
|
const filteredResult = await this.filter(result.getEntities(), async item => {
|
||||||
try {
|
try {
|
||||||
// Check access for each item
|
//skip all the fiels
|
||||||
return await checkAccess(item.id, null, "read", this.repository, context);
|
return await checkAccess(item.id, null, "read", this.repository, context);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.warn("failed to check item access", error);
|
this.logger.warn("failed to check item access", error);
|
||||||
@@ -1017,7 +1034,6 @@ export class DocumentsService {
|
|||||||
|
|
||||||
return new ListResult(result.type, filteredResult, result.nextPage);
|
return new ListResult(result.type, filteredResult, result.nextPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export type SearchDocumentsOptions = {
|
|||||||
fields?: string[];
|
fields?: string[];
|
||||||
onlyDirectlyShared?: boolean;
|
onlyDirectlyShared?: boolean;
|
||||||
onlyUploadedNotByMe?: boolean;
|
onlyUploadedNotByMe?: boolean;
|
||||||
|
pagination?: Paginable;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SearchDocumentsBody = {
|
export type SearchDocumentsBody = {
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { File } from "../../../../services/files/entities/file";
|
|||||||
import { UploadOptions } from "../../../../services/files/types";
|
import { UploadOptions } from "../../../../services/files/types";
|
||||||
import globalResolver from "../../../../services/global-resolver";
|
import globalResolver from "../../../../services/global-resolver";
|
||||||
import {
|
import {
|
||||||
|
CompanyUserRole,
|
||||||
PaginationQueryParameters,
|
PaginationQueryParameters,
|
||||||
ResourceWebsocket,
|
ResourceWebsocket,
|
||||||
CompanyUserRole,
|
|
||||||
} from "../../../../utils/types";
|
} from "../../../../utils/types";
|
||||||
import { DriveFile } from "../../entities/drive-file";
|
import { DriveFile } from "../../entities/drive-file";
|
||||||
import { FileVersion } from "../../entities/file-version";
|
import { FileVersion } from "../../entities/file-version";
|
||||||
@@ -199,48 +199,10 @@ export class DocumentsController {
|
|||||||
onlyUploadedNotByMe: true,
|
onlyUploadedNotByMe: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const data = {
|
return {
|
||||||
...(await globalResolver.services.documents.documents.browse(id, options, context)),
|
...(await globalResolver.services.documents.documents.browse(id, options, context)),
|
||||||
websockets: request.currentUser?.id
|
websockets: [],
|
||||||
? globalResolver.platformServices.realtime.sign(
|
|
||||||
[{ room: `/companies/${context.company.id}/documents/item/${id}` }],
|
|
||||||
request.currentUser?.id,
|
|
||||||
)
|
|
||||||
: [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
sharedWithMe = async (
|
|
||||||
request: FastifyRequest<{
|
|
||||||
Params: RequestParams;
|
|
||||||
Body: SearchDocumentsBody;
|
|
||||||
Querystring: { public_token?: string };
|
|
||||||
}>,
|
|
||||||
): Promise<ListResult<DriveFileDTO>> => {
|
|
||||||
try {
|
|
||||||
const context = getDriveExecutionContext(request);
|
|
||||||
|
|
||||||
const options: SearchDocumentsOptions = {
|
|
||||||
...request.body,
|
|
||||||
company_id: request.body.company_id || context.company.id,
|
|
||||||
view: DriveFileDTOBuilder.VIEW_SHARED_WITH_ME,
|
|
||||||
onlyDirectlyShared: true,
|
|
||||||
onlyUploadedNotByMe: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!Object.keys(options).length) {
|
|
||||||
this.throw500Search();
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileList = await globalResolver.services.documents.documents.search(options, context);
|
|
||||||
|
|
||||||
return this.driveFileDTOBuilder.build(fileList, context, options.fields, options.view);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error({ error: `${error}` }, "error while searching for document");
|
|
||||||
this.throw500Search();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -107,13 +107,6 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, _options, next)
|
|||||||
handler: documentsController.search.bind(documentsController),
|
handler: documentsController.search.bind(documentsController),
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.route({
|
|
||||||
method: "POST",
|
|
||||||
url: `${baseUrl}/shared-with-me`,
|
|
||||||
preValidation: [fastify.authenticate],
|
|
||||||
handler: documentsController.sharedWithMe.bind(documentsController),
|
|
||||||
});
|
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${baseUrl}/browse/:id`,
|
url: `${baseUrl}/browse/:id`,
|
||||||
|
|||||||
@@ -343,15 +343,15 @@ export default class UserApi {
|
|||||||
) {
|
) {
|
||||||
const response = await this.platform.app.inject({
|
const response = await this.platform.app.inject({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${UserApi.DOC_URL}/companies/${this.platform.workspace.company_id}/shared-with-me`,
|
url: `${UserApi.DOC_URL}/companies/${this.platform.workspace.company_id}/browse/shared_with_me`,
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bearer ${this.jwt}`
|
authorization: `Bearer ${this.jwt}`
|
||||||
},
|
},
|
||||||
payload
|
payload
|
||||||
});
|
});
|
||||||
|
|
||||||
return deserialize<SearchResultMockClass>(
|
return deserialize<DriveItemDetailsMockClass>(
|
||||||
SearchResultMockClass,
|
DriveItemDetailsMockClass,
|
||||||
response.body);
|
response.body);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -126,10 +126,32 @@ describe("The Documents Browser Window and API", () => {
|
|||||||
await new Promise(r => setTimeout(r, 3000));
|
await new Promise(r => setTimeout(r, 3000));
|
||||||
|
|
||||||
//then file become searchable
|
//then file become searchable
|
||||||
expect((await anotherUser.browseDocuments("shared_with_me", {})).children).toHaveLength(1);
|
expect((await anotherUser.browseDocuments("shared_with_me", {pageSize: 1})).children).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Share With Me should return all the files that was share by user at one", async () => {
|
||||||
|
const sharedWIthMeFolder = "shared_with_me";
|
||||||
|
const oneUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
|
||||||
|
const anotherUser = await UserApi.getInstance(platform, true, {companyRole: "admin"});
|
||||||
|
|
||||||
|
let files = await oneUser.uploadAllFilesOneByOne();
|
||||||
|
|
||||||
|
await anotherUser.uploadAllFilesOneByOne();
|
||||||
|
await new Promise(r => setTimeout(r, 5000));
|
||||||
|
|
||||||
|
//give permissions to the file
|
||||||
|
files[2].access_info.entities.push({
|
||||||
|
type: "user",
|
||||||
|
id: anotherUser.user.id,
|
||||||
|
level: "read",
|
||||||
|
grantor: null,
|
||||||
|
});
|
||||||
|
await oneUser.updateDocument(files[2].id, files[2]);
|
||||||
|
await new Promise(r => setTimeout(r, 3000));
|
||||||
|
|
||||||
|
//then file become searchable
|
||||||
|
expect((await anotherUser.browseDocuments("shared_with_me", {pagination: {limitStr: 100}})).children).toHaveLength(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ describe("the Drive Search feature", () => {
|
|||||||
const searchResponse = await userTwo.sharedWithMeDocuments({});
|
const searchResponse = await userTwo.sharedWithMeDocuments({});
|
||||||
|
|
||||||
//then::
|
//then::
|
||||||
expect(searchResponse.entities?.length).toEqual(1);
|
expect(searchResponse.children?.length).toEqual(1);
|
||||||
})
|
})
|
||||||
|
|
||||||
it("did search for an item that doesn't exist", async () => {
|
it("did search for an item that doesn't exist", async () => {
|
||||||
@@ -146,7 +146,7 @@ describe("the Drive Search feature", () => {
|
|||||||
|
|
||||||
it("did search a file shared by another user", async () => {
|
it("did search a file shared by another user", async () => {
|
||||||
//then file become searchable
|
//then file become searchable
|
||||||
expect((await userTwo.sharedWithMeDocuments({})).entities).toHaveLength(1);
|
expect((await userTwo.sharedWithMeDocuments({})).children).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("did search a file by file owner", async () => {
|
it("did search a file by file owner", async () => {
|
||||||
|
|||||||
@@ -20,13 +20,61 @@ Content-Type: application/json
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
### Query data
|
||||||
|
GET https://localhost:9200/drive_files/_search
|
||||||
|
Authorization: Basic admin admin
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"query": {
|
||||||
|
"bool": {
|
||||||
|
"boost": 1,
|
||||||
|
"must": [
|
||||||
|
{
|
||||||
|
"bool": {
|
||||||
|
"should": [
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"access_entities": {
|
||||||
|
"query": "7f2e4e80-d4be-11ee-8a11-0544b51cf428",
|
||||||
|
"operator": "AND"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minimum_should_match": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bool": {
|
||||||
|
"should": [
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"company_id": {
|
||||||
|
"query": "7efb7eb1-d4be-11ee-8a11-0544b51cf428",
|
||||||
|
"operator": "AND"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minimum_should_match": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
### Check that it is created
|
### Check that it is created
|
||||||
GET https://localhost:9200/_cat/indices
|
GET https://localhost:9200/_cat/indices
|
||||||
|
Authorization: Basic admin admin
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
### GET index info
|
### GET index info
|
||||||
GET https://localhost:9200/drive_files
|
GET https://localhost:9200/user
|
||||||
|
Authorization: Basic admin admin
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
### GET data to the index
|
### GET data to the index
|
||||||
GET https://localhost:9200/drive_files/_doc/8c03c5a1-0146-11ee-82d9-f503f8a58e9d
|
GET https://localhost:9200/drive_files/_doc/8c03c5a1-0146-11ee-82d9-f503f8a58e9d
|
||||||
|
|||||||
Reference in New Issue
Block a user