refactor: attachment payload typing

This commit is contained in:
Mohamed Marrouchi 2025-01-09 17:40:02 +01:00
parent b57e4693c5
commit 20cf7171fa
14 changed files with 52 additions and 303 deletions

View File

@ -7,10 +7,7 @@
*/ */
import { Subscriber } from '@/chat/schemas/subscriber.schema'; import { Subscriber } from '@/chat/schemas/subscriber.schema';
import { import { AttachmentPayload } from '@/chat/schemas/types/attachment';
AttachmentForeignKey,
AttachmentPayload,
} from '@/chat/schemas/types/attachment';
import { SubscriberChannelData } from '@/chat/schemas/types/channel'; import { SubscriberChannelData } from '@/chat/schemas/types/channel';
import { import {
IncomingMessageType, IncomingMessageType,
@ -251,7 +248,7 @@ export default abstract class EventWrapper<
* *
* @returns Received attachments message * @returns Received attachments message
*/ */
abstract getAttachments(): AttachmentPayload<AttachmentForeignKey>[]; abstract getAttachments(): AttachmentPayload[];
/** /**
* Returns the list of delivered messages * Returns the list of delivered messages
@ -380,7 +377,7 @@ export class GenericEventWrapper extends EventWrapper<
* @returns A list of received attachments * @returns A list of received attachments
* @deprecated - This method is deprecated * @deprecated - This method is deprecated
*/ */
getAttachments(): AttachmentPayload<AttachmentForeignKey>[] { getAttachments(): AttachmentPayload[] {
return []; return [];
} }

View File

@ -92,11 +92,6 @@ const attachment: Attachment = {
updatedAt: new Date(), updatedAt: new Date(),
}; };
const attachmentWithUrl: Attachment = {
...attachment,
url: 'http://localhost:4000/attachment/download/1/attachment.jpg',
};
export const contentMessage: StdOutgoingListMessage = { export const contentMessage: StdOutgoingListMessage = {
options: { options: {
display: OutgoingMessageFormat.list, display: OutgoingMessageFormat.list,
@ -121,7 +116,8 @@ export const contentMessage: StdOutgoingListMessage = {
title: 'First', title: 'First',
desc: 'About being first', desc: 'About being first',
thumbnail: { thumbnail: {
payload: attachmentWithUrl, type: 'image',
payload: { attachment_id: attachment.id },
}, },
getPayload() { getPayload() {
return this.title; return this.title;
@ -136,7 +132,8 @@ export const contentMessage: StdOutgoingListMessage = {
title: 'Second', title: 'Second',
desc: 'About being second', desc: 'About being second',
thumbnail: { thumbnail: {
payload: attachmentWithUrl, type: 'image',
payload: { attachment_id: attachment.id },
}, },
getPayload() { getPayload() {
return this.title; return this.title;
@ -149,14 +146,14 @@ export const contentMessage: StdOutgoingListMessage = {
pagination: { pagination: {
total: 3, total: 3,
skip: 0, skip: 0,
limit: 1, limit: 2,
}, },
}; };
export const attachmentMessage: StdOutgoingAttachmentMessage<Attachment> = { export const attachmentMessage: StdOutgoingAttachmentMessage = {
attachment: { attachment: {
type: FileType.image, type: FileType.image,
payload: attachmentWithUrl, payload: { attachment_id: attachment.id },
}, },
quickReplies: [ quickReplies: [
{ {

View File

@ -6,8 +6,6 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/ */
import { Attachment } from '@/attachment/schemas/attachment.schema';
export enum FileType { export enum FileType {
image = 'image', image = 'image',
video = 'video', video = 'video',
@ -17,14 +15,12 @@ export enum FileType {
} }
export type AttachmentForeignKey = { export type AttachmentForeignKey = {
attachment_id: string; attachment_id: string | null;
/** @deprecated use "attachment_id" instead */ /** @deprecated use "attachment_id" instead */
url?: string; url?: string;
}; };
export interface AttachmentPayload< export interface AttachmentPayload {
A extends Attachment | AttachmentForeignKey,
> {
type: FileType; type: FileType;
payload: A; payload: AttachmentForeignKey;
} }

View File

@ -6,12 +6,11 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/ */
import { Attachment } from '@/attachment/schemas/attachment.schema';
import { PluginName } from '@/plugins/types'; import { PluginName } from '@/plugins/types';
import { Message } from '../message.schema'; import { Message } from '../message.schema';
import { AttachmentForeignKey, AttachmentPayload } from './attachment'; import { AttachmentPayload } from './attachment';
import { Button } from './button'; import { Button } from './button';
import { ContentOptions } from './options'; import { ContentOptions } from './options';
import { StdQuickReply } from './quick-reply'; import { StdQuickReply } from './quick-reply';
@ -96,11 +95,9 @@ export type StdOutgoingListMessage = {
}; };
}; };
export type StdOutgoingAttachmentMessage< export type StdOutgoingAttachmentMessage = {
A extends Attachment | AttachmentForeignKey,
> = {
// Stored in DB as `AttachmentPayload`, `Attachment` when populated for channels relaying // Stored in DB as `AttachmentPayload`, `Attachment` when populated for channels relaying
attachment: AttachmentPayload<A>; attachment: AttachmentPayload;
quickReplies?: StdQuickReply[]; quickReplies?: StdQuickReply[];
}; };
@ -115,7 +112,7 @@ export type BlockMessage =
| StdOutgoingQuickRepliesMessage | StdOutgoingQuickRepliesMessage
| StdOutgoingButtonsMessage | StdOutgoingButtonsMessage
| StdOutgoingListMessage | StdOutgoingListMessage
| StdOutgoingAttachmentMessage<AttachmentForeignKey> | StdOutgoingAttachmentMessage
| StdPluginMessage; | StdPluginMessage;
export type StdOutgoingMessage = export type StdOutgoingMessage =
@ -123,7 +120,7 @@ export type StdOutgoingMessage =
| StdOutgoingQuickRepliesMessage | StdOutgoingQuickRepliesMessage
| StdOutgoingButtonsMessage | StdOutgoingButtonsMessage
| StdOutgoingListMessage | StdOutgoingListMessage
| StdOutgoingAttachmentMessage<Attachment>; | StdOutgoingAttachmentMessage;
type StdIncomingTextMessage = { text: string }; type StdIncomingTextMessage = { text: string };
@ -142,9 +139,7 @@ export type StdIncomingLocationMessage = {
export type StdIncomingAttachmentMessage = { export type StdIncomingAttachmentMessage = {
type: PayloadType.attachments; type: PayloadType.attachments;
serialized_text: string; serialized_text: string;
attachment: attachment: AttachmentPayload | AttachmentPayload[];
| AttachmentPayload<AttachmentForeignKey>
| AttachmentPayload<AttachmentForeignKey>[];
}; };
export type StdIncomingMessage = export type StdIncomingMessage =
@ -189,7 +184,7 @@ export interface StdOutgoingListEnvelope {
export interface StdOutgoingAttachmentEnvelope { export interface StdOutgoingAttachmentEnvelope {
format: OutgoingMessageFormat.attachment; format: OutgoingMessageFormat.attachment;
message: StdOutgoingAttachmentMessage<Attachment>; message: StdOutgoingAttachmentMessage;
} }
export type StdOutgoingEnvelope = export type StdOutgoingEnvelope =

View File

@ -6,7 +6,7 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/ */
import { AttachmentForeignKey, AttachmentPayload } from './attachment'; import { AttachmentPayload } from './attachment';
import { PayloadType } from './message'; import { PayloadType } from './message';
export type Payload = export type Payload =
@ -19,7 +19,7 @@ export type Payload =
} }
| { | {
type: PayloadType.attachments; type: PayloadType.attachments;
attachments: AttachmentPayload<AttachmentForeignKey>; attachments: AttachmentPayload;
}; };
export enum QuickReplyType { export enum QuickReplyType {

View File

@ -526,24 +526,12 @@ export class BlockService extends BaseService<Block, BlockPopulate, BlockFull> {
throw new Error('Remote attachments are no longer supported!'); throw new Error('Remote attachments are no longer supported!');
} }
const attachment = await this.attachmentService.findOne(
attachmentPayload.attachment_id,
);
if (!attachment) {
this.logger.debug(
'Unable to locate the attachment for the given block',
block,
);
throw new Error('Unable to find attachment.');
}
const envelope: StdOutgoingEnvelope = { const envelope: StdOutgoingEnvelope = {
format: OutgoingMessageFormat.attachment, format: OutgoingMessageFormat.attachment,
message: { message: {
attachment: { attachment: {
type: blockMessage.attachment.type, type: blockMessage.attachment.type,
payload: attachment, payload: blockMessage.attachment.payload,
}, },
quickReplies: blockMessage.quickReplies quickReplies: blockMessage.quickReplies
? [...blockMessage.quickReplies] ? [...blockMessage.quickReplies]

View File

@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Hexastack. All rights reserved. * Copyright © 2025 Hexastack. All rights reserved.
* *
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@ -13,11 +13,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { AttachmentRepository } from '@/attachment/repositories/attachment.repository'; import { AttachmentRepository } from '@/attachment/repositories/attachment.repository';
import { AttachmentModel } from '@/attachment/schemas/attachment.schema'; import { AttachmentModel } from '@/attachment/schemas/attachment.schema';
import { AttachmentService } from '@/attachment/services/attachment.service'; import { AttachmentService } from '@/attachment/services/attachment.service';
import { FileType } from '@/chat/schemas/types/attachment'; import { OutgoingMessageFormat } from '@/chat/schemas/types/message';
import {
ContentElement,
OutgoingMessageFormat,
} from '@/chat/schemas/types/message';
import { ContentOptions } from '@/chat/schemas/types/options'; import { ContentOptions } from '@/chat/schemas/types/options';
import { LoggerService } from '@/logger/logger.service'; import { LoggerService } from '@/logger/logger.service';
import { IGNORED_TEST_FIELDS } from '@/utils/test/constants'; import { IGNORED_TEST_FIELDS } from '@/utils/test/constants';
@ -43,7 +39,6 @@ describe('ContentService', () => {
let contentService: ContentService; let contentService: ContentService;
let contentTypeService: ContentTypeService; let contentTypeService: ContentTypeService;
let contentRepository: ContentRepository; let contentRepository: ContentRepository;
let attachmentService: AttachmentService;
beforeAll(async () => { beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
@ -69,7 +64,6 @@ describe('ContentService', () => {
contentService = module.get<ContentService>(ContentService); contentService = module.get<ContentService>(ContentService);
contentTypeService = module.get<ContentTypeService>(ContentTypeService); contentTypeService = module.get<ContentTypeService>(ContentTypeService);
contentRepository = module.get<ContentRepository>(ContentRepository); contentRepository = module.get<ContentRepository>(ContentRepository);
attachmentService = module.get<AttachmentService>(AttachmentService);
}); });
afterAll(async () => { afterAll(async () => {
@ -111,103 +105,6 @@ describe('ContentService', () => {
}); });
}); });
describe('getAttachmentIds', () => {
const contents: Content[] = [
{
id: '1',
title: 'store 1',
entity: 'stores',
status: true,
dynamicFields: {
image: {
type: FileType.image,
payload: {
attachment_id: '123',
},
},
},
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: '2',
title: 'store 2',
entity: 'stores',
status: true,
dynamicFields: {
image: {
type: FileType.image,
payload: {
attachment_id: '456',
},
},
},
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: '3',
title: 'store 3',
entity: 'stores',
status: true,
dynamicFields: {
image: {
type: FileType.image,
payload: {
url: 'https://remote.file/image.jpg',
},
},
},
createdAt: new Date(),
updatedAt: new Date(),
},
];
it('should return all content attachment ids', () => {
const result = contentService.getAttachmentIds(
contents.map(Content.toElement),
'image',
);
expect(result).toEqual(['123', '456']);
});
it('should not return any of the attachment ids', () => {
const result = contentService.getAttachmentIds(contents, 'file');
expect(result).toEqual([]);
});
});
describe('populateAttachments', () => {
it('should return populated content', async () => {
const storeContents = await contentService.find({ title: /^store/ });
const elements: ContentElement[] = await Promise.all(
storeContents.map(Content.toElement).map(async (store) => {
const attachmentId = store.image.payload.attachment_id;
if (attachmentId) {
const attachment = await attachmentService.findOne(attachmentId);
if (attachment) {
return {
...store,
image: {
type: 'image',
payload: {
...attachment,
url: `http://localhost:4000/attachment/download/${attachment.id}/${attachment.name}`,
},
},
};
}
}
return store;
}),
);
const result = await contentService.populateAttachments(
storeContents.map(Content.toElement),
'image',
);
expect(result).toEqualPayload(elements);
});
});
describe('getContent', () => { describe('getContent', () => {
const contentOptions: ContentOptions = { const contentOptions: ContentOptions = {
display: OutgoingMessageFormat.list, display: OutgoingMessageFormat.list,

View File

@ -8,12 +8,8 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Attachment } from '@/attachment/schemas/attachment.schema';
import { AttachmentService } from '@/attachment/services/attachment.service'; import { AttachmentService } from '@/attachment/services/attachment.service';
import { import { StdOutgoingListMessage } from '@/chat/schemas/types/message';
ContentElement,
StdOutgoingListMessage,
} from '@/chat/schemas/types/message';
import { ContentOptions } from '@/chat/schemas/types/options'; import { ContentOptions } from '@/chat/schemas/types/options';
import { LoggerService } from '@/logger/logger.service'; import { LoggerService } from '@/logger/logger.service';
import { BaseService } from '@/utils/generics/base-service'; import { BaseService } from '@/utils/generics/base-service';
@ -51,93 +47,6 @@ export class ContentService extends BaseService<
return await this.repository.textSearch(query); 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. * Retrieves content based on the provided options and pagination settings.
* *
@ -175,21 +84,6 @@ export class ContentService extends BaseService<
sort: ['createdAt', 'desc'], sort: ['createdAt', 'desc'],
}); });
const elements = contents.map(Content.toElement); 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 { return {
elements, elements,
pagination: { pagination: {

View File

@ -985,14 +985,14 @@ export default abstract class BaseWebChannelHandler<
* @returns A ready to be sent attachment message * @returns A ready to be sent attachment message
*/ */
async _attachmentFormat( async _attachmentFormat(
message: StdOutgoingAttachmentMessage<Attachment>, message: StdOutgoingAttachmentMessage,
_options?: BlockOptions, _options?: BlockOptions,
): Promise<Web.OutgoingMessageBase> { ): Promise<Web.OutgoingMessageBase> {
const payload: Web.OutgoingMessageBase = { const payload: Web.OutgoingMessageBase = {
type: Web.OutgoingMessageType.file, type: Web.OutgoingMessageType.file,
data: { data: {
type: message.attachment.type, type: message.attachment.type,
url: await this.getPublicUrl(message.attachment.payload.id), url: await this.getPublicUrl(message.attachment.payload.attachment_id),
}, },
}; };
if (message.quickReplies && message.quickReplies.length > 0) { if (message.quickReplies && message.quickReplies.length > 0) {
@ -1034,14 +1034,7 @@ export default abstract class BaseWebChannelHandler<
if (fields.image_url && item[fields.image_url]) { if (fields.image_url && item[fields.image_url]) {
const attachmentPayload = item[fields.image_url] const attachmentPayload = item[fields.image_url]
.payload as AttachmentForeignKey; .payload as AttachmentForeignKey;
if (attachmentPayload.url) { if (attachmentPayload.attachment_id) {
if (!attachmentPayload.attachment_id) {
// @deprecated
this.logger.warn(
'Web Channel Handler: Attachment remote url has been deprecated',
item,
);
}
element.image_url = await this.getPublicUrl( element.image_url = await this.getPublicUrl(
attachmentPayload.attachment_id, attachmentPayload.attachment_id,
); );

View File

@ -9,10 +9,7 @@
import { Attachment } from '@/attachment/schemas/attachment.schema'; import { Attachment } from '@/attachment/schemas/attachment.schema';
import EventWrapper from '@/channel/lib/EventWrapper'; import EventWrapper from '@/channel/lib/EventWrapper';
import { ChannelName } from '@/channel/types'; import { ChannelName } from '@/channel/types';
import { import { AttachmentPayload } from '@/chat/schemas/types/attachment';
AttachmentForeignKey,
AttachmentPayload,
} from '@/chat/schemas/types/attachment';
import { import {
IncomingMessageType, IncomingMessageType,
PayloadType, PayloadType,
@ -71,9 +68,11 @@ type WebEventAdapter =
}; };
// eslint-disable-next-line prettier/prettier // eslint-disable-next-line prettier/prettier
export default class WebEventWrapper< export default class WebEventWrapper<N extends ChannelName> extends EventWrapper<
N extends ChannelName, WebEventAdapter,
> extends EventWrapper<WebEventAdapter, Web.Event, N> { Web.Event,
N
> {
/** /**
* Constructor : channel's event wrapper * Constructor : channel's event wrapper
* *
@ -298,7 +297,7 @@ export default class WebEventWrapper<
* @deprecated * @deprecated
* @returns Received attachments message * @returns Received attachments message
*/ */
getAttachments(): AttachmentPayload<AttachmentForeignKey>[] { getAttachments(): AttachmentPayload[] {
const message = this.getMessage() as any; const message = this.getMessage() as any;
return 'attachment' in message ? [].concat(message.attachment) : []; return 'attachment' in message ? [].concat(message.attachment) : [];
} }

View File

@ -15,7 +15,6 @@ import { DialogTitle } from "@/app-components/dialogs";
import { useDialog } from "@/hooks/useDialog"; import { useDialog } from "@/hooks/useDialog";
import { useTranslate } from "@/hooks/useTranslate"; import { useTranslate } from "@/hooks/useTranslate";
import { import {
AttachmentAttrs,
FileType, FileType,
StdIncomingAttachmentMessage, StdIncomingAttachmentMessage,
StdOutgoingAttachmentMessage, StdOutgoingAttachmentMessage,
@ -93,9 +92,7 @@ const componentMap: { [key in FileType]: FC<AttachmentInterface> } = {
}; };
export const AttachmentViewer = (props: { export const AttachmentViewer = (props: {
message: message: StdIncomingAttachmentMessage | StdOutgoingAttachmentMessage;
| StdIncomingAttachmentMessage
| StdOutgoingAttachmentMessage<AttachmentAttrs>;
}) => { }) => {
const message = props.message; const message = props.message;

View File

@ -1,11 +1,12 @@
/* /*
* Copyright © 2024 Hexastack. All rights reserved. * Copyright © 2025 Hexastack. All rights reserved.
* *
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/ */
import { IBlockAttributes } from "@/types/block.types"; import { IBlockAttributes } from "@/types/block.types";
import { import {
ButtonType, ButtonType,
@ -35,7 +36,7 @@ export const ATTACHMENT_BLOCK_TEMPLATE: Partial<IBlockAttributes> = {
message: { message: {
attachment: { attachment: {
type: FileType.unknown, type: FileType.unknown,
payload: { attachment_id: undefined }, payload: { attachment_id: null },
}, },
quickReplies: [], quickReplies: [],
}, },

View File

@ -1,17 +1,17 @@
/* /*
* Copyright © 2024 Hexastack. All rights reserved. * Copyright © 2025 Hexastack. All rights reserved.
* *
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/ */
import { EntityType, Format } from "@/services/types"; import { EntityType, Format } from "@/services/types";
import { IBaseSchema, IFormat, OmitPopulate } from "./base.types"; import { IBaseSchema, IFormat, OmitPopulate } from "./base.types";
import { ILabel } from "./label.types"; import { ILabel } from "./label.types";
import { import {
AttachmentForeignKey,
ContentOptions, ContentOptions,
PayloadType, PayloadType,
StdOutgoingAttachmentMessage, StdOutgoingAttachmentMessage,
@ -57,7 +57,7 @@ export type BlockMessage =
| StdOutgoingQuickRepliesMessage | StdOutgoingQuickRepliesMessage
| StdOutgoingButtonsMessage | StdOutgoingButtonsMessage
| StdOutgoingListMessage | StdOutgoingListMessage
| StdOutgoingAttachmentMessage<AttachmentForeignKey> | StdOutgoingAttachmentMessage
| StdPluginMessage; | StdPluginMessage;
export interface PayloadPattern { export interface PayloadPattern {

View File

@ -52,15 +52,14 @@ export interface AttachmentAttrs {
} }
export type AttachmentForeignKey = { export type AttachmentForeignKey = {
attachment_id: string | null;
/** @deprecated use attachment_id instead */
url?: string; url?: string;
attachment_id: string | undefined;
}; };
export interface AttachmentPayload< export interface AttachmentPayload {
A extends AttachmentAttrs | AttachmentForeignKey,
> {
type: FileType; type: FileType;
payload?: A; payload: AttachmentForeignKey;
} }
// Content // Content
@ -96,7 +95,7 @@ export type Payload =
} }
| { | {
type: PayloadType.attachments; type: PayloadType.attachments;
attachments: AttachmentPayload<AttachmentForeignKey>; attachments: AttachmentPayload;
}; };
export enum QuickReplyType { export enum QuickReplyType {
@ -163,11 +162,9 @@ export type StdOutgoingListMessage = {
limit: number; limit: number;
}; };
}; };
export type StdOutgoingAttachmentMessage< export type StdOutgoingAttachmentMessage = {
A extends AttachmentAttrs | AttachmentForeignKey,
> = {
// Stored in DB as `AttachmentPayload`, `Attachment` when populated for channels relaying // Stored in DB as `AttachmentPayload`, `Attachment` when populated for channels relaying
attachment: AttachmentPayload<A>; attachment: AttachmentPayload;
quickReplies?: StdQuickReply[]; quickReplies?: StdQuickReply[];
}; };
@ -190,9 +187,7 @@ export type StdIncomingLocationMessage = {
export type StdIncomingAttachmentMessage = { export type StdIncomingAttachmentMessage = {
type: PayloadType.attachments; type: PayloadType.attachments;
serialized_text: string; serialized_text: string;
attachment: attachment: AttachmentPayload | AttachmentPayload[];
| AttachmentPayload<AttachmentForeignKey>
| AttachmentPayload<AttachmentForeignKey>[];
}; };
export type StdPluginMessage = { export type StdPluginMessage = {
@ -211,7 +206,7 @@ export type StdOutgoingMessage =
| StdOutgoingQuickRepliesMessage | StdOutgoingQuickRepliesMessage
| StdOutgoingButtonsMessage | StdOutgoingButtonsMessage
| StdOutgoingListMessage | StdOutgoingListMessage
| StdOutgoingAttachmentMessage<AttachmentAttrs>; | StdOutgoingAttachmentMessage;
export interface IMessageAttributes { export interface IMessageAttributes {
mid?: string; mid?: string;