Merge pull request #546 from Hexastack/refactor/attachment-payload
Some checks are pending
Build and Push Docker API Image / build-and-push (push) Waiting to run
Build and Push Docker Base Image / build-and-push (push) Waiting to run
Build and Push Docker UI Image / build-and-push (push) Waiting to run

feat: Refactor attachment payload + use public signed urls in web channel
This commit is contained in:
Med Marrouchi
2025-01-14 19:12:00 +01:00
committed by GitHub
38 changed files with 692 additions and 576 deletions

View File

@@ -8,12 +8,8 @@
import { Injectable } from '@nestjs/common';
import { Attachment } from '@/attachment/schemas/attachment.schema';
import { AttachmentService } from '@/attachment/services/attachment.service';
import {
ContentElement,
StdOutgoingListMessage,
} from '@/chat/schemas/types/message';
import { StdOutgoingListMessage } from '@/chat/schemas/types/message';
import { ContentOptions } from '@/chat/schemas/types/options';
import { LoggerService } from '@/logger/logger.service';
import { BaseService } from '@/utils/generics/base-service';
@@ -53,93 +49,6 @@ export class ContentService extends BaseService<
return await this.repository.textSearch(query);
}
/**
* Extracts attachment IDs from content entities, issuing warnings for any issues.
*
* @param contents - An array of content entities.
* @param attachmentFieldName - The name of the attachment field to check for.
*
* @return A list of attachment IDs.
*/
getAttachmentIds(contents: ContentElement[], attachmentFieldName: string) {
return contents.reduce((acc, content) => {
if (attachmentFieldName in content) {
const attachment = content[attachmentFieldName];
if (
typeof attachment === 'object' &&
'attachment_id' in attachment.payload
) {
acc.push(attachment.payload.attachment_id);
} else {
this.logger.error(
`Remote attachments have been deprecated, content "${content.title}" is missing the "attachment_id"`,
);
}
} else {
this.logger.warn(
`Field "${attachmentFieldName}" not found in content "${content.title}"`,
);
}
return acc;
}, [] as string[]);
}
/**
* Populates attachment fields within content entities with detailed attachment information.
*
* @param elements - An array of content entities.
* @param attachmentFieldName - The name of the attachment field to populate.
*
* @return A list of content with populated attachment data.
*/
async populateAttachments(
elements: ContentElement[],
attachmentFieldName: string,
): Promise<ContentElement[]> {
const attachmentIds = this.getAttachmentIds(elements, attachmentFieldName);
if (attachmentIds.length > 0) {
const attachments = await this.attachmentService.find({
_id: { $in: attachmentIds },
});
const attachmentsById = attachments.reduce(
(acc, curr) => {
acc[curr.id] = curr;
return acc;
},
{} as { [key: string]: Attachment },
);
const populatedContents = elements.map((content) => {
const attachmentField = content[attachmentFieldName];
if (
typeof attachmentField === 'object' &&
'attachment_id' in attachmentField.payload
) {
const attachmentId = attachmentField?.payload?.attachment_id;
return {
...content,
[attachmentFieldName]: {
type: attachmentField.type,
payload: {
...(attachmentsById[attachmentId] || attachmentField.payload),
url: Attachment.getAttachmentUrl(
attachmentId,
attachmentsById[attachmentId].name,
),
},
},
};
} else {
return content;
}
});
return populatedContents;
}
return elements;
}
/**
* Retrieves content based on the provided options and pagination settings.
*
@@ -177,21 +86,6 @@ export class ContentService extends BaseService<
sort: ['createdAt', 'desc'],
});
const elements = contents.map(Content.toElement);
const attachmentFieldName = options.fields.image_url;
if (attachmentFieldName) {
// Populate attachment when there's an image field
return {
elements: await this.populateAttachments(
elements,
attachmentFieldName,
),
pagination: {
total,
skip,
limit,
},
};
}
return {
elements,
pagination: {