#11370 & #11402 ### Changes made: 1. Updated search.service.ts to properly handle workspace member avatar and Person Avatar URLs with authentication tokens 2. Integrated FileService for token generation 3. Added FileModule to SearchModule for dependency injection ### Implementation details: - Used getImageUrlWithToken to append authentication tokens to avatar URLs specifically for workspace members --------- Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
This commit is contained in:
@ -27,7 +27,7 @@ export class FileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async encodeFileToken(payloadToEncode: Record<string, any>) {
|
encodeFileToken(payloadToEncode: Record<string, any>) {
|
||||||
const fileTokenExpiresIn = this.twentyConfigService.get(
|
const fileTokenExpiresIn = this.twentyConfigService.get(
|
||||||
'FILE_TOKEN_EXPIRES_IN',
|
'FILE_TOKEN_EXPIRES_IN',
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
|
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/search/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/search/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||||
|
|
||||||
@ -8,7 +9,7 @@ describe('SearchService', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [SearchService],
|
providers: [SearchService, { provide: FileService, useValue: {} }],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<SearchService>(SearchService);
|
service = module.get<SearchService>(SearchService);
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||||
|
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||||
import { SearchResolver } from 'src/engine/core-modules/search/search.resolver';
|
import { SearchResolver } from 'src/engine/core-modules/search/search.resolver';
|
||||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [WorkspaceCacheStorageModule, FeatureFlagModule],
|
imports: [WorkspaceCacheStorageModule, FeatureFlagModule, FileModule],
|
||||||
providers: [SearchResolver, SearchService],
|
providers: [SearchResolver, SearchService],
|
||||||
})
|
})
|
||||||
export class SearchModule {}
|
export class SearchModule {}
|
||||||
|
|||||||
@ -118,6 +118,7 @@ export class SearchResolver {
|
|||||||
return this.searchService.computeSearchObjectResults(
|
return this.searchService.computeSearchObjectResults(
|
||||||
allRecordsWithObjectMetadataItems,
|
allRecordsWithObjectMetadataItems,
|
||||||
limit,
|
limit,
|
||||||
|
workspace.id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { Brackets, ObjectLiteral } from 'typeorm';
|
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
import { getLogoUrlFromDomainName } from 'twenty-shared/utils';
|
import { getLogoUrlFromDomainName } from 'twenty-shared/utils';
|
||||||
|
import { Brackets, ObjectLiteral } from 'typeorm';
|
||||||
|
|
||||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||||
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
|
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||||
|
|
||||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||||
|
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||||
import { RESULTS_LIMIT_BY_OBJECT_WITHOUT_SEARCH_TERMS } from 'src/engine/core-modules/search/constants/results-limit-by-object-without-search-terms';
|
import { RESULTS_LIMIT_BY_OBJECT_WITHOUT_SEARCH_TERMS } from 'src/engine/core-modules/search/constants/results-limit-by-object-without-search-terms';
|
||||||
import { STANDARD_OBJECTS_BY_PRIORITY_RANK } from 'src/engine/core-modules/search/constants/standard-objects-by-priority-rank';
|
import { STANDARD_OBJECTS_BY_PRIORITY_RANK } from 'src/engine/core-modules/search/constants/standard-objects-by-priority-rank';
|
||||||
import { ObjectRecordFilterInput } from 'src/engine/core-modules/search/dtos/object-record-filter-input';
|
import { ObjectRecordFilterInput } from 'src/engine/core-modules/search/dtos/object-record-filter-input';
|
||||||
@ -24,6 +25,8 @@ import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SearchService {
|
export class SearchService {
|
||||||
|
constructor(private readonly fileService: FileService) {}
|
||||||
|
|
||||||
filterObjectMetadataItems({
|
filterObjectMetadataItems({
|
||||||
objectMetadataItemWithFieldMaps,
|
objectMetadataItemWithFieldMaps,
|
||||||
includedObjectNameSingulars,
|
includedObjectNameSingulars,
|
||||||
@ -194,9 +197,18 @@ export class SearchService {
|
|||||||
].name;
|
].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getImageUrlWithToken(avatarUrl: string, workspaceId: string): string {
|
||||||
|
const avatarUrlToken = this.fileService.encodeFileToken({
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return `${avatarUrl}?token=${avatarUrlToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
getImageIdentifierValue(
|
getImageIdentifierValue(
|
||||||
record: ObjectRecord,
|
record: ObjectRecord,
|
||||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps,
|
objectMetadataItem: ObjectMetadataItemWithFieldMaps,
|
||||||
|
workspaceId: string,
|
||||||
): string {
|
): string {
|
||||||
const imageIdentifierField =
|
const imageIdentifierField =
|
||||||
this.getImageIdentifierColumn(objectMetadataItem);
|
this.getImageIdentifierColumn(objectMetadataItem);
|
||||||
@ -205,12 +217,15 @@ export class SearchService {
|
|||||||
return getLogoUrlFromDomainName(record.domainNamePrimaryLinkUrl) || '';
|
return getLogoUrlFromDomainName(record.domainNamePrimaryLinkUrl) || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageIdentifierField ? record[imageIdentifierField] : '';
|
return imageIdentifierField
|
||||||
|
? this.getImageUrlWithToken(record[imageIdentifierField], workspaceId)
|
||||||
|
: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
computeSearchObjectResults(
|
computeSearchObjectResults(
|
||||||
recordsWithObjectMetadataItems: RecordsWithObjectMetadataItem[],
|
recordsWithObjectMetadataItems: RecordsWithObjectMetadataItem[],
|
||||||
limit: number,
|
limit: number,
|
||||||
|
workspaceId: string,
|
||||||
) {
|
) {
|
||||||
const searchRecords = recordsWithObjectMetadataItems.flatMap(
|
const searchRecords = recordsWithObjectMetadataItems.flatMap(
|
||||||
({ objectMetadataItem, records }) => {
|
({ objectMetadataItem, records }) => {
|
||||||
@ -219,7 +234,11 @@ export class SearchService {
|
|||||||
recordId: record.id,
|
recordId: record.id,
|
||||||
objectNameSingular: objectMetadataItem.nameSingular,
|
objectNameSingular: objectMetadataItem.nameSingular,
|
||||||
label: this.getLabelIdentifierValue(record, objectMetadataItem),
|
label: this.getLabelIdentifierValue(record, objectMetadataItem),
|
||||||
imageUrl: this.getImageIdentifierValue(record, objectMetadataItem),
|
imageUrl: this.getImageIdentifierValue(
|
||||||
|
record,
|
||||||
|
objectMetadataItem,
|
||||||
|
workspaceId,
|
||||||
|
),
|
||||||
tsRankCD: record.tsRankCD,
|
tsRankCD: record.tsRankCD,
|
||||||
tsRank: record.tsRank,
|
tsRank: record.tsRank,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -80,6 +80,7 @@ export const SEARCH_FIELDS_FOR_WORKSPACE_MEMBER: FieldTypeAndNameMetadata[] = [
|
|||||||
description: msg`A workspace member`,
|
description: msg`A workspace member`,
|
||||||
icon: STANDARD_OBJECT_ICONS.workspaceMember,
|
icon: STANDARD_OBJECT_ICONS.workspaceMember,
|
||||||
labelIdentifierStandardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.name,
|
labelIdentifierStandardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.name,
|
||||||
|
imageIdentifierStandardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.avatarUrl,
|
||||||
})
|
})
|
||||||
@WorkspaceIsSystem()
|
@WorkspaceIsSystem()
|
||||||
@WorkspaceIsNotAuditLogged()
|
@WorkspaceIsNotAuditLogged()
|
||||||
|
|||||||
Reference in New Issue
Block a user