From 2d4554440ed9329a5876a9c674c3eb2de0f2f917 Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni <77477100+suneettipirneni@users.noreply.github.com> Date: Wed, 16 Feb 2022 08:01:41 -0500 Subject: [PATCH] fix: use case converter for json component serialization (#7464) Co-authored-by: Vlad Frangu --- packages/discord.js/package.json | 1 + .../discord.js/src/structures/ActionRow.js | 6 ++-- .../src/structures/ButtonComponent.js | 4 +-- packages/discord.js/src/structures/Embed.js | 4 +-- .../src/structures/MessagePayload.js | 7 ++-- .../src/structures/SelectMenuComponent.js | 4 +-- packages/discord.js/src/util/Components.js | 26 --------------- packages/discord.js/src/util/Embeds.js | 33 ------------------- packages/discord.js/src/util/Transformers.js | 20 +++++++++++ packages/discord.js/typings/index.d.ts | 10 ------ yarn.lock | 8 +++++ 11 files changed, 40 insertions(+), 83 deletions(-) create mode 100644 packages/discord.js/src/util/Transformers.js diff --git a/packages/discord.js/package.json b/packages/discord.js/package.json index 9fd91738b..29eac30b1 100644 --- a/packages/discord.js/package.json +++ b/packages/discord.js/package.json @@ -53,6 +53,7 @@ "@sapphire/snowflake": "^3.1.0", "@types/ws": "^8.2.2", "discord-api-types": "^0.27.0", + "lodash.snakecase": "^4.1.1", "node-fetch": "^2.6.7", "ws": "^8.5.0" }, diff --git a/packages/discord.js/src/structures/ActionRow.js b/packages/discord.js/src/structures/ActionRow.js index 2f050f770..450a9f325 100644 --- a/packages/discord.js/src/structures/ActionRow.js +++ b/packages/discord.js/src/structures/ActionRow.js @@ -1,13 +1,11 @@ 'use strict'; const { ActionRow: BuildersActionRow } = require('@discordjs/builders'); -const Components = require('../util/Components'); +const Transformers = require('../util/Transformers'); class ActionRow extends BuildersActionRow { constructor(data) { - // TODO: Simplify when getters PR is merged. - const initData = Components.transformJSON(data); - super({ ...initData, components: initData.components ?? [] }); + super(Transformers.toSnakeCase(data)); } } diff --git a/packages/discord.js/src/structures/ButtonComponent.js b/packages/discord.js/src/structures/ButtonComponent.js index e25705399..a6cce1082 100644 --- a/packages/discord.js/src/structures/ButtonComponent.js +++ b/packages/discord.js/src/structures/ButtonComponent.js @@ -1,11 +1,11 @@ 'use strict'; const { ButtonComponent: BuildersButtonComponent } = require('@discordjs/builders'); -const Components = require('../util/Components'); +const Transformers = require('../util/Transformers'); class ButtonComponent extends BuildersButtonComponent { constructor(data) { - super(Components.transformJSON(data)); + super(Transformers.toSnakeCase(data)); } } diff --git a/packages/discord.js/src/structures/Embed.js b/packages/discord.js/src/structures/Embed.js index e95ebf595..b20023751 100644 --- a/packages/discord.js/src/structures/Embed.js +++ b/packages/discord.js/src/structures/Embed.js @@ -1,11 +1,11 @@ 'use strict'; const { Embed: BuildersEmbed } = require('@discordjs/builders'); -const Embeds = require('../util/Embeds'); +const Transformers = require('../util/Transformers'); class Embed extends BuildersEmbed { constructor(data) { - super({ ...Embeds.transformJSON(data) }); + super(Transformers.toSnakeCase(data)); } } diff --git a/packages/discord.js/src/structures/MessagePayload.js b/packages/discord.js/src/structures/MessagePayload.js index ef6ea622e..bfa7372ee 100644 --- a/packages/discord.js/src/structures/MessagePayload.js +++ b/packages/discord.js/src/structures/MessagePayload.js @@ -4,10 +4,9 @@ const { Buffer } = require('node:buffer'); const { Embed, isJSONEncodable } = require('@discordjs/builders'); const { MessageFlags } = require('discord-api-types/v9'); const { RangeError } = require('../errors'); -const Components = require('../util/Components'); const DataResolver = require('../util/DataResolver'); -const Embeds = require('../util/Embeds'); const MessageFlagsBitField = require('../util/MessageFlagsBitField'); +const Transformers = require('../util/Transformers'); const Util = require('../util/Util'); /** @@ -134,7 +133,7 @@ class MessagePayload { } const components = this.options.components?.map(c => - isJSONEncodable(c) ? c.toJSON() : Components.transformJSON(c), + isJSONEncodable(c) ? c.toJSON() : Transformers.toSnakeCase(c), ); let username; @@ -195,7 +194,7 @@ class MessagePayload { tts, nonce, embeds: this.options.embeds?.map(embed => - embed instanceof Embed ? embed.toJSON() : Embeds.transformJSON(embed), + embed instanceof Embed ? embed.toJSON() : Transformers.toSnakeCase(embed), ), components, username, diff --git a/packages/discord.js/src/structures/SelectMenuComponent.js b/packages/discord.js/src/structures/SelectMenuComponent.js index dc7dec046..77589626a 100644 --- a/packages/discord.js/src/structures/SelectMenuComponent.js +++ b/packages/discord.js/src/structures/SelectMenuComponent.js @@ -1,11 +1,11 @@ 'use strict'; const { SelectMenuComponent: BuildersSelectMenuComponent } = require('@discordjs/builders'); -const Components = require('../util/Components'); +const Transformers = require('../util/Transformers'); class SelectMenuComponent extends BuildersSelectMenuComponent { constructor(data) { - super(Components.transformJSON(data)); + super(Transformers.toSnakeCase(data)); } } diff --git a/packages/discord.js/src/util/Components.js b/packages/discord.js/src/util/Components.js index 52330d409..59a816be3 100644 --- a/packages/discord.js/src/util/Components.js +++ b/packages/discord.js/src/util/Components.js @@ -42,29 +42,3 @@ /** * @typedef {ActionRowData|ButtonComponentData|SelectMenuComponentData} ComponentData */ - -class Components extends null { - /** - * Transforms json data into api-compatible json data. - * @param {ComponentData|APIMessageComponent} data The data to transform. - * @returns {APIMessageComponentData} - */ - static transformJSON(data) { - return { - type: data?.type, - custom_id: data?.customId ?? data?.custom_id, - disabled: data?.disabled, - style: data?.style, - label: data?.label, - emoji: data?.emoji, - url: data?.url, - options: data?.options, - placeholder: data?.placeholder, - min_values: data?.minValues ?? data?.min_values, - max_values: data?.maxValues ?? data?.max_values, - components: data?.components?.map(c => Components.transformJSON(c)), - }; - } -} - -module.exports = Components; diff --git a/packages/discord.js/src/util/Embeds.js b/packages/discord.js/src/util/Embeds.js index b0cf3a3cd..d6294b2fe 100644 --- a/packages/discord.js/src/util/Embeds.js +++ b/packages/discord.js/src/util/Embeds.js @@ -46,36 +46,3 @@ * @property {string} value * @property {?boolean} inline */ - -class Embeds extends null { - /** - * Transforms json data into api-compatible json data. - * @param {EmbedData|APIEmbed} data The data to transform. - * @returns {APIEmbed} - */ - static transformJSON(data) { - return { - title: data?.title, - type: data?.type, - description: data?.description, - url: data?.url, - timestamp: data?.timestamp, - color: data?.color, - footer: { - test: data?.footer?.text, - icon_url: data?.footer?.iconURL ?? data?.footer?.icon_url, - }, - image: data?.image, - thumbnail: data?.thumbnail, - provider: data?.provider, - author: { - name: data?.author?.name, - text: data?.author?.text, - icon_url: data?.author?.iconURL ?? data?.author?.icon_url, - }, - fields: data?.fields, - }; - } -} - -module.exports = Embeds; diff --git a/packages/discord.js/src/util/Transformers.js b/packages/discord.js/src/util/Transformers.js new file mode 100644 index 000000000..f9a6a40f4 --- /dev/null +++ b/packages/discord.js/src/util/Transformers.js @@ -0,0 +1,20 @@ +'use strict'; + +const snakeCase = require('lodash.snakecase'); + +class Transformers extends null { + /** + * Transforms camel-cased keys into snake cased keys + * @param {*} obj The object to transform + * @returns {*} + */ + static toSnakeCase(obj = {}) { + if (typeof obj !== 'object' || !obj) return obj; + if (Array.isArray(obj)) return obj.map(Transformers.toSnakeCase); + return Object.fromEntries( + Object.entries(obj).map(([key, value]) => [snakeCase(key), Transformers.toSnakeCase(value)]), + ); + } +} + +module.exports = Transformers; diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index c2b0a9aac..203b80ded 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -2411,16 +2411,6 @@ export class Formatters extends null { export type ComponentData = ActionRowComponentData | ButtonComponentData | SelectMenuComponentData; -export class Components extends null { - private constructor(); - public static transformJSON(data: ComponentData | APIMessageComponent): APIMessageComponent; -} - -export class Embeds extends null { - private constructor(); - public static transformJSON(data: EmbedData | APIEmbed): APIEmbed; -} - export class VoiceChannel extends BaseGuildVoiceChannel { public readonly speakable: boolean; public type: ChannelType.GuildVoice; diff --git a/yarn.lock b/yarn.lock index 5c6530c6a..479404d59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4441,6 +4441,7 @@ __metadata: husky: ^7.0.4 is-ci: ^3.0.1 jest: ^27.5.1 + lodash.snakecase: ^4.1.1 node-fetch: ^2.6.7 prettier: ^2.5.1 tsd: ^0.19.1 @@ -7610,6 +7611,13 @@ dts-critic@latest: languageName: node linkType: hard +"lodash.snakecase@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.snakecase@npm:4.1.1" + checksum: 1685ed3e83dda6eae5a4dcaee161a51cd210aabb3e1c09c57150e7dd8feda19e4ca0d27d0631eabe8d0f4eaa51e376da64e8c018ae5415417c5890d42feb72a8 + languageName: node + linkType: hard + "lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21"