Fix - Remove signFileUrl method (#19121)

`signFileUrl` is the old way to resolve file url. Replaced by
`signFileByIdUrl` which needs `FileFolder` and `fileId`.

Fix also avatar for person and workspaceMember. To be continued with
imageIdentifier refactor.


Fix : 
- https://discord.com/channels/1130383047699738754/1486723854910099576
- https://discord.com/channels/1130383047699738754/1484566445584285870
- https://discord.com/channels/1130383047699738754/1487124612889313420

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
This commit is contained in:
Etienne
2026-03-30 17:14:54 +02:00
committed by GitHub
parent 3d1c53ec9d
commit ecf8161d0e
20 changed files with 175 additions and 195 deletions
@@ -5,7 +5,7 @@ import {
mockFlatFieldMetadataMaps,
mockFlatObjectMetadatas,
} from 'src/engine/core-modules/__mocks__/mockFlatObjectMetadatas';
import { FileService } from 'src/engine/core-modules/file/services/file.service';
import { FileUrlService } from 'src/engine/core-modules/file/file-url/file-url.service';
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@@ -18,7 +18,7 @@ describe('SearchService', () => {
providers: [
SearchService,
{ provide: GlobalWorkspaceOrmManager, useValue: {} },
{ provide: FileService, useValue: {} },
{ provide: FileUrlService, useValue: {} },
{ provide: TwentyConfigService, useValue: { get: () => false } },
],
}).compile();
@@ -105,13 +105,13 @@ describe('SearchService', () => {
});
describe('getImageIdentifierColumn', () => {
it('should return null if the object metadata item does not have an image identifier', () => {
it('should return `avatarFile` if the object metadata item is a person', () => {
const imageIdentifierColumn = service.getImageIdentifierColumn(
mockFlatObjectMetadatas[0],
mockFlatFieldMetadataMaps,
);
expect(imageIdentifierColumn).toBeNull();
expect(imageIdentifierColumn).toEqual('avatarFile');
});
it('should return `domainNamePrimaryLinkUrl` column for a company object metadata item', () => {
const imageIdentifierColumn = service.getImageIdentifierColumn(
@@ -3,18 +3,24 @@ import { Injectable } from '@nestjs/common';
import { isNonEmptyString } from '@sniptt/guards';
import chunk from 'lodash.chunk';
import { OBJECTS_WITH_CHANNEL_VISIBILITY_CONSTRAINTS } from 'twenty-shared/constants';
import { FieldMetadataType, ObjectRecord } from 'twenty-shared/types';
import {
FieldMetadataType,
FileFolder,
ObjectRecord,
} from 'twenty-shared/types';
import { getLogoUrlFromDomainName, isDefined } from 'twenty-shared/utils';
import { Brackets, type ObjectLiteral } from 'typeorm';
import { type ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
import { FileOutput } from 'src/engine/api/common/common-args-processors/data-arg-processor/types/file-item.type';
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
import {
decodeCursor,
encodeCursorData,
} from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util';
import { FileService } from 'src/engine/core-modules/file/services/file.service';
import { FileUrlService } from 'src/engine/core-modules/file/file-url/file-url.service';
import { extractFileIdFromUrl } from 'src/engine/core-modules/file/files-field/utils/extract-file-id-from-url.util';
import { STANDARD_OBJECTS_BY_PRIORITY_RANK } from 'src/engine/core-modules/search/constants/standard-objects-by-priority-rank';
import { type ObjectRecordFilterInput } from 'src/engine/core-modules/search/dtos/object-record-filter-input';
import { type SearchArgs } from 'src/engine/core-modules/search/dtos/search-args';
@@ -52,7 +58,7 @@ const OBJECT_METADATA_ITEMS_CHUNK_SIZE = 5;
export class SearchService {
constructor(
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
private readonly fileService: FileService,
private readonly fileUrlService: FileUrlService,
private readonly twentyConfigService: TwentyConfigService,
) {}
@@ -512,6 +518,15 @@ export class SearchService {
return 'domainNamePrimaryLinkUrl';
}
//TODO: Temporary solution before imageIdentifier refactor
if (flatObjectMetadata.nameSingular === 'person') {
return 'avatarFile';
}
if (flatObjectMetadata.nameSingular === 'workspaceMember') {
return 'avatarUrl';
}
if (!flatObjectMetadata.imageIdentifierFieldMetadataId) {
return null;
}
@@ -528,10 +543,15 @@ export class SearchService {
return imageIdentifierField.name;
}
private getImageUrlWithToken(avatarUrl: string, workspaceId: string): string {
return this.fileService.signFileUrl({
url: avatarUrl,
private getImageUrlWithToken(
avatarFileId: string,
fileFolder: FileFolder,
workspaceId: string,
): string {
return this.fileUrlService.signFileByIdUrl({
fileId: avatarFileId,
workspaceId,
fileFolder,
});
}
@@ -553,9 +573,41 @@ export class SearchService {
return getLogoUrlFromDomainName(record.domainNamePrimaryLinkUrl) || '';
}
//TODO: Temporary solution before imageIdentifier refactor
if (flatObjectMetadata.nameSingular === 'person') {
const avatarFileId = (record.avatarFile as FileOutput[])?.[0]?.fileId;
if (!isDefined(avatarFileId)) {
return '';
}
return this.getImageUrlWithToken(
avatarFileId,
FileFolder.FilesField,
workspaceId,
);
}
if (flatObjectMetadata.nameSingular === 'workspaceMember') {
const avatarFileId = extractFileIdFromUrl(
record.avatarUrl,
FileFolder.CorePicture,
);
if (!isDefined(avatarFileId)) {
return '';
}
return this.getImageUrlWithToken(
avatarFileId,
FileFolder.CorePicture,
workspaceId,
);
}
return imageIdentifierField &&
isNonEmptyString(record[imageIdentifierField])
? this.getImageUrlWithToken(record[imageIdentifierField], workspaceId)
? this.getImageUrlWithToken(
record[imageIdentifierField],
FileFolder.FilesField,
workspaceId,
)
: '';
}