mirror of
https://github.com/discordjs/discord.js.git
synced 2026-07-21 13:42:56 +00:00
feat: file_types for builders (#11579)
* feat: `file_types` * fix: .js extension Co-authored-by: Almeida <github@almeidx.dev> * fix: adding * test: use `Reflect` * fix: update deprecated usage --------- Co-authored-by: Almeida <github@almeidx.dev> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { APIFileUploadComponent } from 'discord-api-types/v10';
|
||||
import type { APIFileUploadComponent, FileUploadType } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { FileUploadBuilder } from '../../src/components/fileUpload/FileUpload.js';
|
||||
@@ -15,6 +15,16 @@ describe('File Upload Components', () => {
|
||||
expect(() => {
|
||||
fileUploadComponent().setCustomId('foobar').setMinValues(2).setMaxValues(9).toJSON();
|
||||
}).not.toThrowError();
|
||||
|
||||
expect(() => {
|
||||
fileUploadComponent().setCustomId('foobar').setFileTypes('audio', 'image', 'video', '.pdf').toJSON();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('GIVEN file types THEN they can be added', () => {
|
||||
expect(
|
||||
fileUploadComponent().setCustomId('foobar').addFileTypes('image').addFileTypes(['.pdf']).toJSON().file_types,
|
||||
).toEqual(['image', '.pdf']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,6 +44,30 @@ describe('File Upload Components', () => {
|
||||
expect(() => {
|
||||
fileUploadComponent().setCustomId('a').setMinValues(-1).toJSON();
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
fileUploadComponent()
|
||||
.setCustomId('a')
|
||||
.setFileTypes(Array.from({ length: 11 }, () => '.txt' as const))
|
||||
.toJSON();
|
||||
}).toThrow();
|
||||
|
||||
for (const invalidFileType of ['document', 'pdf', '.']) {
|
||||
expect(() => {
|
||||
fileUploadComponent()
|
||||
.setCustomId('a')
|
||||
.setFileTypes(invalidFileType as FileUploadType)
|
||||
.toJSON();
|
||||
}).toThrow();
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
new FileUploadBuilder({
|
||||
type: ComponentType.FileUpload,
|
||||
custom_id: 'a',
|
||||
file_types: ['document' as FileUploadType],
|
||||
}).toJSON();
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('GIVEN valid input THEN valid JSON outputs are given', () => {
|
||||
@@ -42,6 +76,7 @@ describe('File Upload Components', () => {
|
||||
custom_id: 'custom id',
|
||||
min_values: 5,
|
||||
max_values: 6,
|
||||
file_types: ['image', '.pdf'],
|
||||
required: false,
|
||||
} satisfies APIFileUploadComponent;
|
||||
|
||||
@@ -52,8 +87,11 @@ describe('File Upload Components', () => {
|
||||
.setCustomId(fileUploadData.custom_id)
|
||||
.setMaxValues(fileUploadData.max_values)
|
||||
.setMinValues(fileUploadData.min_values)
|
||||
.setFileTypes(fileUploadData.file_types)
|
||||
.setRequired(fileUploadData.required)
|
||||
.toJSON(),
|
||||
).toEqual(fileUploadData);
|
||||
|
||||
expect(new FileUploadBuilder(fileUploadData).clearFileTypes().toJSON().file_types).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
type APIApplicationCommandRoleOption,
|
||||
type APIApplicationCommandStringOption,
|
||||
type APIApplicationCommandUserOption,
|
||||
type FileUploadType,
|
||||
} from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import {
|
||||
@@ -222,11 +223,45 @@ describe('Application Command toJSON() results', () => {
|
||||
});
|
||||
|
||||
test('GIVEN an attachment option THEN calling toJSON should return a valid JSON', () => {
|
||||
expect(getAttachmentOption().toJSON()).toEqual<APIApplicationCommandAttachmentOption>({
|
||||
expect(
|
||||
getAttachmentOption().setFileTypes(['image', '.pdf']).toJSON(),
|
||||
).toEqual<APIApplicationCommandAttachmentOption>({
|
||||
name: 'attachment',
|
||||
description: 'attachment',
|
||||
type: ApplicationCommandOptionType.Attachment,
|
||||
required: true,
|
||||
file_types: ['image', '.pdf'],
|
||||
});
|
||||
});
|
||||
|
||||
test('GIVEN attachment option file types THEN they can be added', () => {
|
||||
expect(
|
||||
Reflect.get(getAttachmentOption().addFileTypes('image').addFileTypes(['.pdf']).toJSON(), 'file_types'),
|
||||
).toEqual(['image', '.pdf']);
|
||||
});
|
||||
|
||||
test('GIVEN attachment option file types THEN they can be cleared', () => {
|
||||
expect(
|
||||
Reflect.get(getAttachmentOption().setFileTypes('audio', '.ogg').clearFileTypes().toJSON(), 'file_types'),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test('GIVEN too many attachment option file types THEN calling toJSON should throw', () => {
|
||||
expect(() =>
|
||||
getAttachmentOption()
|
||||
.setFileTypes(Array.from({ length: 11 }, () => '.txt' as const))
|
||||
.toJSON(),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test.each(['document', 'pdf', '.'])(
|
||||
'GIVEN invalid attachment file type %s THEN calling toJSON should throw',
|
||||
(fileType) => {
|
||||
expect(() =>
|
||||
getAttachmentOption()
|
||||
.setFileTypes(fileType as FileUploadType)
|
||||
.toJSON(),
|
||||
).toThrow();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -5,6 +5,11 @@ export const idPredicate = z.int().min(0).max(2_147_483_647).optional();
|
||||
export const customIdPredicate = z.string().min(1).max(100);
|
||||
export const snowflakePredicate = z.string().regex(/^(?:0|[1-9]\d*)$/);
|
||||
|
||||
export const fileUploadTypesPredicate = z
|
||||
.union([z.enum(['audio', 'image', 'video']), z.string().min(2).startsWith('.')])
|
||||
.array()
|
||||
.max(10);
|
||||
|
||||
export const memberPermissionsPredicate = z.coerce.bigint();
|
||||
|
||||
export const localeMapPredicate = z.strictObject(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { z } from 'zod';
|
||||
import { customIdPredicate, idPredicate } from '../../Assertions';
|
||||
import { customIdPredicate, fileUploadTypesPredicate, idPredicate } from '../../Assertions.js';
|
||||
|
||||
export const fileUploadPredicate = z.object({
|
||||
type: z.literal(ComponentType.FileUpload),
|
||||
@@ -8,5 +8,6 @@ export const fileUploadPredicate = z.object({
|
||||
custom_id: customIdPredicate,
|
||||
min_values: z.int().min(0).max(10).optional(),
|
||||
max_values: z.int().min(1).max(10).optional(),
|
||||
file_types: fileUploadTypesPredicate.optional(),
|
||||
required: z.boolean().optional(),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { APIFileUploadComponent } from 'discord-api-types/v10';
|
||||
import type { APIFileUploadComponent, FileUploadType } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
|
||||
import { validate } from '../../util/validation.js';
|
||||
import { ComponentBuilder } from '../Component.js';
|
||||
import { fileUploadPredicate } from './Assertions.js';
|
||||
@@ -24,6 +25,7 @@ export class FileUploadBuilder extends ComponentBuilder<APIFileUploadComponent>
|
||||
* custom_id: "file_upload",
|
||||
* min_values: 2,
|
||||
* max_values: 5,
|
||||
* file_types: ["image", ".pdf"],
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
@@ -33,7 +35,9 @@ export class FileUploadBuilder extends ComponentBuilder<APIFileUploadComponent>
|
||||
* custom_id: "file_upload",
|
||||
* min_values: 2,
|
||||
* max_values: 5,
|
||||
* }).setRequired();
|
||||
* })
|
||||
* .setFileTypes("image", ".pdf")
|
||||
* .setRequired();
|
||||
* ```
|
||||
*/
|
||||
public constructor(data: Partial<APIFileUploadComponent> = {}) {
|
||||
@@ -87,6 +91,41 @@ export class FileUploadBuilder extends ComponentBuilder<APIFileUploadComponent>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds file types allowed in this file upload.
|
||||
*
|
||||
* @remarks
|
||||
* When specifying only extensions, include `.jpg` for image uploads and both `.mp4` and `.mov`
|
||||
* for video uploads due to mobile platform limitations.
|
||||
* @param fileTypes - The file groups or dot-prefixed extensions to allow
|
||||
*/
|
||||
public addFileTypes(...fileTypes: RestOrArray<FileUploadType>) {
|
||||
this.data.file_types ??= [];
|
||||
this.data.file_types.push(...normalizeArray(fileTypes));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file types allowed in this file upload.
|
||||
*
|
||||
* @remarks
|
||||
* When specifying only extensions, include `.jpg` for image uploads and both `.mp4` and `.mov`
|
||||
* for video uploads due to mobile platform limitations.
|
||||
* @param fileTypes - The file groups or dot-prefixed extensions to allow
|
||||
*/
|
||||
public setFileTypes(...fileTypes: RestOrArray<FileUploadType>) {
|
||||
this.data.file_types = normalizeArray(fileTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the file types allowed in this file upload.
|
||||
*/
|
||||
public clearFileTypes() {
|
||||
this.data.file_types = undefined;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this file upload is required.
|
||||
*
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ApplicationCommandType,
|
||||
} from 'discord-api-types/v10';
|
||||
import { z } from 'zod';
|
||||
import { localeMapPredicate, memberPermissionsPredicate } from '../../../Assertions.js';
|
||||
import { fileUploadTypesPredicate, localeMapPredicate, memberPermissionsPredicate } from '../../../Assertions.js';
|
||||
import { ApplicationCommandOptionAllowedChannelTypes } from './mixins/ApplicationCommandOptionChannelTypesMixin.js';
|
||||
|
||||
const namePredicate = z
|
||||
@@ -77,6 +77,7 @@ export const baseBasicOptionPredicate = z.object({
|
||||
export const attachmentOptionPredicate = z.object({
|
||||
...baseBasicOptionPredicate.shape,
|
||||
type: z.literal(ApplicationCommandOptionType.Attachment),
|
||||
file_types: fileUploadTypesPredicate.optional(),
|
||||
});
|
||||
|
||||
export const booleanOptionPredicate = z.object({
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { ApplicationCommandOptionType } from 'discord-api-types/v10';
|
||||
import {
|
||||
ApplicationCommandOptionType,
|
||||
type APIApplicationCommandAttachmentOption,
|
||||
type FileUploadType,
|
||||
} from 'discord-api-types/v10';
|
||||
import { normalizeArray, type RestOrArray } from '../../../../util/normalizeArray.js';
|
||||
import { attachmentOptionPredicate } from '../Assertions.js';
|
||||
import { ApplicationCommandOptionBase } from './ApplicationCommandOptionBase.js';
|
||||
import type { ApplicationCommandOptionBaseData } from './ApplicationCommandOptionBase.js';
|
||||
|
||||
/**
|
||||
* A chat input command attachment option.
|
||||
@@ -11,10 +17,51 @@ export class ChatInputCommandAttachmentOption extends ApplicationCommandOptionBa
|
||||
*/
|
||||
protected static override readonly predicate = attachmentOptionPredicate;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
declare protected readonly data: ApplicationCommandOptionBaseData &
|
||||
Partial<Pick<APIApplicationCommandAttachmentOption, 'file_types'>>;
|
||||
|
||||
/**
|
||||
* Creates a new attachment option.
|
||||
*/
|
||||
public constructor() {
|
||||
super(ApplicationCommandOptionType.Attachment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds file types allowed for this attachment option.
|
||||
*
|
||||
* @remarks
|
||||
* When specifying only extensions, include `.jpg` for image uploads and both `.mp4` and `.mov`
|
||||
* for video uploads due to mobile platform limitations.
|
||||
* @param fileTypes - The file groups or dot-prefixed extensions to allow
|
||||
*/
|
||||
public addFileTypes(...fileTypes: RestOrArray<FileUploadType>) {
|
||||
this.data.file_types ??= [];
|
||||
this.data.file_types.push(...normalizeArray(fileTypes));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file types allowed for this attachment option.
|
||||
*
|
||||
* @remarks
|
||||
* When specifying only extensions, include `.jpg` for image uploads and both `.mp4` and `.mov`
|
||||
* for video uploads due to mobile platform limitations.
|
||||
* @param fileTypes - The file groups or dot-prefixed extensions to allow
|
||||
*/
|
||||
public setFileTypes(...fileTypes: RestOrArray<FileUploadType>) {
|
||||
this.data.file_types = normalizeArray(fileTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the file types allowed for this attachment option.
|
||||
*/
|
||||
public clearFileTypes() {
|
||||
this.data.file_types = undefined;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user