Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: remove obsolete builder methods #7590

Merged
merged 3 commits into from
Mar 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const getChannelOption = () =>
.setName('owo')
.setDescription('Testing 123')
.setRequired(true)
.addChannelType(ChannelType.GuildText);
.addChannelTypes(ChannelType.GuildText);

const getStringOption = () =>
new SlashCommandStringOption().setName('owo').setDescription('Testing 123').setRequired(true);
Expand Down Expand Up @@ -100,7 +100,7 @@ describe('Application Command toJSON() results', () => {
});

expect(
getIntegerOption().addChoice({ name: 'uwu', value: 1 }).toJSON(),
getIntegerOption().addChoices({ name: 'uwu', value: 1 }).toJSON(),
).toEqual<APIApplicationCommandIntegerOption>({
name: 'owo',
description: 'Testing 123',
Expand Down Expand Up @@ -143,15 +143,17 @@ describe('Application Command toJSON() results', () => {
choices: [],
});

expect(getNumberOption().addChoice({ name: 'uwu', value: 1 }).toJSON()).toEqual<APIApplicationCommandNumberOption>({
name: 'owo',
description: 'Testing 123',
type: ApplicationCommandOptionType.Number,
required: true,
max_value: 10,
min_value: -1.23,
choices: [{ name: 'uwu', value: 1 }],
});
expect(getNumberOption().addChoices({ name: 'uwu', value: 1 }).toJSON()).toEqual<APIApplicationCommandNumberOption>(
{
name: 'owo',
description: 'Testing 123',
type: ApplicationCommandOptionType.Number,
required: true,
max_value: 10,
min_value: -1.23,
choices: [{ name: 'uwu', value: 1 }],
},
);
});

test('GIVEN a role option THEN calling toJSON should return a valid JSON', () => {
Expand Down Expand Up @@ -182,7 +184,7 @@ describe('Application Command toJSON() results', () => {
});

expect(
getStringOption().addChoice({ name: 'uwu', value: '1' }).toJSON(),
getStringOption().addChoices({ name: 'uwu', value: '1' }).toJSON(),
).toEqual<APIApplicationCommandStringOption>({
name: 'owo',
description: 'Testing 123',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,16 @@ describe('Slash Commands', () => {
test('GIVEN valid array of options or choices THEN does not throw error', () => {
expect(() => SlashCommandAssertions.validateMaxOptionsLength([])).not.toThrowError();

expect(() => SlashCommandAssertions.validateMaxChoicesLength([])).not.toThrowError();
expect(() => SlashCommandAssertions.validateChoicesLength(25, [])).not.toThrowError();
});

test('GIVEN invalid options or choices THEN throw error', () => {
expect(() => SlashCommandAssertions.validateMaxOptionsLength(null)).toThrowError();

expect(() => SlashCommandAssertions.validateMaxChoicesLength(null)).toThrowError();

// Given an array that's too big
expect(() => SlashCommandAssertions.validateMaxOptionsLength(largeArray)).toThrowError();

expect(() => SlashCommandAssertions.validateMaxChoicesLength(largeArray)).toThrowError();
expect(() => SlashCommandAssertions.validateChoicesLength(1, largeArray)).toThrowError();
});

test('GIVEN valid required parameters THEN does not throw error', () => {
Expand Down Expand Up @@ -179,31 +177,25 @@ describe('Slash Commands', () => {
test('GIVEN a builder with both choices and autocomplete THEN does throw an error', () => {
expect(() =>
getBuilder().addStringOption(
// @ts-expect-error Checking if check works JS-side too
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
getStringOption().setAutocomplete(true).addChoice('Fancy Pants', 'fp_1'),
getStringOption().setAutocomplete(true).addChoices({ name: 'Fancy Pants', value: 'fp_1' }),
),
).toThrowError();

expect(() =>
getBuilder().addStringOption(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
getStringOption()
.setAutocomplete(true)
// @ts-expect-error Checking if check works JS-side too
.addChoices([
['Fancy Pants', 'fp_1'],
['Fancy Shoes', 'fs_1'],
['The Whole shebang', 'all'],
]),
.addChoices(
{ name: 'Fancy Pants', value: 'fp_1' },
{ name: 'Fancy Shoes', value: 'fs_1' },
{ name: 'The Whole shebang', value: 'all' },
),
),
).toThrowError();

expect(() =>
getBuilder().addStringOption(
// @ts-expect-error Checking if check works JS-side too
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
getStringOption().addChoice('Fancy Pants', 'fp_1').setAutocomplete(true),
getStringOption().addChoices({ name: 'Fancy Pants', value: 'fp_1' }).setAutocomplete(true),
),
).toThrowError();

Expand Down Expand Up @@ -231,20 +223,20 @@ describe('Slash Commands', () => {

test('GIVEN a builder with valid channel options and channel_types THEN does not throw an error', () => {
expect(() =>
getBuilder().addChannelOption(getChannelOption().addChannelType(ChannelType.GuildText)),
getBuilder().addChannelOption(getChannelOption().addChannelTypes(ChannelType.GuildText)),
).not.toThrowError();

expect(() => {
getBuilder().addChannelOption(
getChannelOption().addChannelTypes([ChannelType.GuildNews, ChannelType.GuildText]),
getChannelOption().addChannelTypes(ChannelType.GuildNews, ChannelType.GuildText),
);
}).not.toThrowError();
});

test('GIVEN a builder with valid channel options and channel_types THEN does throw an error', () => {
expect(() => getBuilder().addChannelOption(getChannelOption().addChannelType(100))).toThrowError();
expect(() => getBuilder().addChannelOption(getChannelOption().addChannelTypes(100))).toThrowError();

expect(() => getBuilder().addChannelOption(getChannelOption().addChannelTypes([100, 200]))).toThrowError();
expect(() => getBuilder().addChannelOption(getChannelOption().addChannelTypes(100, 200))).toThrowError();
});

test('GIVEN a builder with invalid number min/max options THEN does throw an error', () => {
Expand Down
20 changes: 7 additions & 13 deletions packages/builders/docs/examples/Slash Command Builders.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,7 @@ const boopCommand = new SlashCommandBuilder()
option
.setName('boop_reminder')
.setDescription('How often should we remind you to boop the user')
.addChoice('Every day', 1)
.addChoice('Weekly', 7)
// Or, if you prefer adding more choices at once, you can use an array
.addChoices([
['Every three months', 90],
['Yearly', 365],
]),
.addChoices({ name: 'Every day', value: 1 }, { name: 'Weekly', value: 7 }),
);

// Get the final raw data that can be sent to Discord
Expand Down Expand Up @@ -71,11 +65,11 @@ const pointsCommand = new SlashCommandBuilder()
option
.setName('action')
.setDescription('What action should be taken with the users points?')
.addChoices([
['Add points', 'add'],
['Remove points', 'remove'],
['Reset points', 'reset'],
])
.addChoices(
{ name: 'Add points', value: 'add' },
{ name: 'Remove points', value: 'remove' },
{ name: 'Reset points', value: 'reset' },
)
.setRequired(true),
)
.addIntegerOption((option) => option.setName('points').setDescription('Points to add or remove')),
Expand All @@ -102,4 +96,4 @@ const pointsCommand = new SlashCommandBuilder()

// Get the final raw data that can be sent to Discord
const rawData = pointsCommand.toJSON();
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ export function validateRequired(required: unknown): asserts required is boolean
booleanPredicate.parse(required);
}

export function validateMaxChoicesLength(choices: APIApplicationCommandOptionChoice[]) {
maxArrayLengthPredicate.parse(choices);
const choicesLengthPredicate = z.number().lte(25);

export function validateChoicesLength(amountAdding: number, choices?: APIApplicationCommandOptionChoice[]): void {
choicesLengthPredicate.parse((choices?.length ?? 0) + amountAdding);
}

export function assertReturnOfBuilder<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,33 @@ const allowedChannelTypes = [

export type ApplicationCommandOptionAllowedChannelTypes = typeof allowedChannelTypes[number];

const channelTypePredicate = z.union(
allowedChannelTypes.map((type) => z.literal(type)) as [
ZodLiteral<ChannelType>,
ZodLiteral<ChannelType>,
...ZodLiteral<ChannelType>[]
],
const channelTypesPredicate = z.array(
z.union(
allowedChannelTypes.map((type) => z.literal(type)) as [
ZodLiteral<ChannelType>,
ZodLiteral<ChannelType>,
...ZodLiteral<ChannelType>[]
],
),
);

export class ApplicationCommandOptionChannelTypesMixin {
public readonly channel_types?: ApplicationCommandOptionAllowedChannelTypes[];

/**
* Adds a channel type to this option
* Adds channel types to this option
*
* @param channelType The type of channel to allow
* @param channelTypes The channel types to add
*/
public addChannelType(channelType: ApplicationCommandOptionAllowedChannelTypes) {
public addChannelTypes(...channelTypes: ApplicationCommandOptionAllowedChannelTypes[]) {
if (this.channel_types === undefined) {
Reflect.set(this, 'channel_types', []);
}

channelTypePredicate.parse(channelType);
this.channel_types!.push(channelType);
channelTypesPredicate.parse(channelTypes);

return this;
}
this.channel_types!.push(...channelTypes);

/**
* Adds channel types to this option
*
* @param channelTypes The channel types to add
*/
public addChannelTypes(channelTypes: ApplicationCommandOptionAllowedChannelTypes[]) {
channelTypes.forEach((channelType) => this.addChannelType(channelType));
return this;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { APIApplicationCommandOptionChoice, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { z } from 'zod';
import { validateMaxChoicesLength } from '../Assertions';
import { validateChoicesLength } from '../Assertions';

const stringPredicate = z.string().min(1).max(100);
const numberPredicate = z.number().gt(-Infinity).lt(Infinity);
Expand All @@ -17,50 +17,34 @@ export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends s
public readonly type!: ApplicationCommandOptionType;

/**
* Adds a choice for this option
* Adds multiple choices for this option
*
* @param choice The choice to add
* @param choices The choices to add
*/
public addChoice(choice: APIApplicationCommandOptionChoice<T>): this {
const { name, value } = choice;
if (this.autocomplete) {
public addChoices(...choices: APIApplicationCommandOptionChoice<T>[]): this {
if (choices.length > 0 && this.autocomplete) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}

choicesPredicate.parse(choices);

if (this.choices === undefined) {
Reflect.set(this, 'choices', []);
}

validateMaxChoicesLength(this.choices!);
validateChoicesLength(choices.length, this.choices);

// Validate name
stringPredicate.parse(name);
for (const { name, value } of choices) {
// Validate the value
if (this.type === ApplicationCommandOptionType.String) {
stringPredicate.parse(value);
} else {
numberPredicate.parse(value);
}

// Validate the value
if (this.type === ApplicationCommandOptionType.String) {
stringPredicate.parse(value);
} else {
numberPredicate.parse(value);
this.choices!.push({ name, value });
}

this.choices!.push({ name, value });

return this;
}

/**
* Adds multiple choices for this option
*
* @param choices The choices to add
*/
public addChoices(...choices: APIApplicationCommandOptionChoice<T>[]): this {
if (this.autocomplete) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}

choicesPredicate.parse(choices);

for (const entry of choices) this.addChoice(entry);
return this;
}

Expand All @@ -72,7 +56,7 @@ export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends s
choicesPredicate.parse(choices);

Reflect.set(this, 'choices', []);
for (const entry of choices) this.addChoice(entry);
this.addChoices(...choices);

return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
input:
| SlashCommandStringOption
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| Omit<SlashCommandStringOption, 'addChoice' | 'addChoices'>
| Omit<SlashCommandStringOption, 'addChoices'>
| ((
builder: SlashCommandStringOption,
) =>
| SlashCommandStringOption
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| Omit<SlashCommandStringOption, 'addChoice' | 'addChoices'>),
| Omit<SlashCommandStringOption, 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandStringOption);
}
Expand All @@ -105,13 +105,13 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
input:
| SlashCommandIntegerOption
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| Omit<SlashCommandIntegerOption, 'addChoice' | 'addChoices'>
| Omit<SlashCommandIntegerOption, 'addChoices'>
| ((
builder: SlashCommandIntegerOption,
) =>
| SlashCommandIntegerOption
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| Omit<SlashCommandIntegerOption, 'addChoice' | 'addChoices'>),
| Omit<SlashCommandIntegerOption, 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandIntegerOption);
}
Expand All @@ -125,13 +125,13 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
input:
| SlashCommandNumberOption
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| Omit<SlashCommandNumberOption, 'addChoice' | 'addChoices'>
| Omit<SlashCommandNumberOption, 'addChoices'>
| ((
builder: SlashCommandNumberOption,
) =>
| SlashCommandNumberOption
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| Omit<SlashCommandNumberOption, 'addChoice' | 'addChoices'>),
| Omit<SlashCommandNumberOption, 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandNumberOption);
}
Expand All @@ -140,8 +140,8 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
input:
| T
| Omit<T, 'setAutocomplete'>
| Omit<T, 'addChoice' | 'addChoices'>
| ((builder: T) => T | Omit<T, 'setAutocomplete'> | Omit<T, 'addChoice' | 'addChoices'>),
| Omit<T, 'addChoices'>
| ((builder: T) => T | Omit<T, 'setAutocomplete'> | Omit<T, 'addChoices'>),
Instance: new () => T,
): ShouldOmitSubcommandFunctions extends true ? Omit<this, 'addSubcommand' | 'addSubcommandGroup'> : this {
const { options } = this;
Expand Down