From 08972ee879f2ec5048601b38f83729dd0b568373 Mon Sep 17 00:00:00 2001 From: Billy Stalnaker Date: Tue, 25 Mar 2025 13:14:22 -0400 Subject: [PATCH] adds unique has column because of mysql unique character limitation Signed-off-by: Billy Stalnaker --- .../migrations/20250317_addTopic.js | 27 ++++++++++++++----- plugins/notifications-backend/report.sql.md | 17 ++++++------ .../database/DatabaseNotificationsStore.ts | 14 ++++++++++ plugins/notifications-common/report.api.md | 8 ++++++ plugins/notifications-common/src/utils.ts | 12 +++++++++ 5 files changed, 63 insertions(+), 15 deletions(-) diff --git a/plugins/notifications-backend/migrations/20250317_addTopic.js b/plugins/notifications-backend/migrations/20250317_addTopic.js index fc1b51985f..c8f78a5640 100644 --- a/plugins/notifications-backend/migrations/20250317_addTopic.js +++ b/plugins/notifications-backend/migrations/20250317_addTopic.js @@ -13,29 +13,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const crypto = require('crypto'); + exports.up = async function up(knex) { await knex.schema.alterTable('user_settings', table => { table.string('topic').nullable().after('origin'); - }); - - await knex.schema.table('user_settings', table => { + table.string('settings_key_hash', 64).notNullable(); table.dropUnique([], 'user_settings_unique_idx'); }); await knex.schema.alterTable('user_settings', table => { - table.unique(['user(255)', 'channel(255)', 'origin(255)', 'topic(255)'], { - indexName: 'user_settings_unique_idx', - }); + table.unique(['settings_key_hash'], 'user_settings_unique_idx'); }); + + const rows = await knex('user_settings').select('user', 'channel', 'origin'); + for (const row of rows) { + const rawKey = `${row.user}|${row.channel}|${row.origin}|}`; + const hash = crypto.createHash('sha256').update(rawKey).digest('hex'); + await knex('user_settings') + .where({ + user: row.user, + channel: row.channel, + origin: row.origin, + topic: row.topic, + }) + .update({ settings_key_hash: hash }); + } }; exports.down = async function down(knex) { await knex.schema.table('user_settings', table => { table.dropUnique([], 'user_settings_unique_idx'); + table.dropColumn('settings_key_hash'); + table.dropColumn('topic'); }); await knex.schema.alterTable('user_settings', table => { - table.dropColumn('topic'); table.unique(['user', 'channel', 'origin'], { indexName: 'user_settings_unique_idx', }); diff --git a/plugins/notifications-backend/report.sql.md b/plugins/notifications-backend/report.sql.md index 6f3495b265..aacb787a61 100644 --- a/plugins/notifications-backend/report.sql.md +++ b/plugins/notifications-backend/report.sql.md @@ -63,15 +63,16 @@ ## Table `user_settings` -| Column | Type | Nullable | Max Length | Default | -| --------- | ------------------- | -------- | ---------- | ------- | -| `channel` | `character varying` | false | 255 | - | -| `enabled` | `boolean` | false | - | `true` | -| `origin` | `character varying` | false | 255 | - | -| `topic` | `character varying` | true | 255 | - | -| `user` | `character varying` | false | 255 | - | +| Column | Type | Nullable | Max Length | Default | +| ------------------- | ------------------- | -------- | ---------- | ------- | +| `channel` | `character varying` | false | 255 | - | +| `enabled` | `boolean` | false | - | `true` | +| `origin` | `character varying` | false | 255 | - | +| `settings_key_hash` | `character varying` | false | 64 | - | +| `topic` | `character varying` | true | 255 | - | +| `user` | `character varying` | false | 255 | - | ### Indices -- `user_settings_unique_idx` (`user`, `channel`, `origin`, `topic`) unique +- `user_settings_unique_idx` (`settings_key_hash`) unique - `user_settings_user_idx` (`user`) diff --git a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts index d4bc881fbb..da5fd5d349 100644 --- a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts +++ b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts @@ -24,6 +24,7 @@ import { TopicGetOptions, } from './NotificationsStore'; import { + generateSettingsHash, Notification, NotificationSettings, notificationSeverities, @@ -584,6 +585,7 @@ export class DatabaseNotificationsStore implements NotificationsStore { settings: NotificationSettings; }): Promise { const rows: { + settings_key_hash: string; user: string; channel: string; origin: string; @@ -594,6 +596,12 @@ export class DatabaseNotificationsStore implements NotificationsStore { options.settings.channels.forEach(channel => { channel.origins.forEach(origin => { rows.push({ + settings_key_hash: generateSettingsHash( + options.user, + channel.id, + origin.id, + null, + ), user: options.user, channel: channel.id, origin: origin.id, @@ -603,6 +611,12 @@ export class DatabaseNotificationsStore implements NotificationsStore { origin.topics?.forEach(topic => { rows.push({ + settings_key_hash: generateSettingsHash( + options.user, + channel.id, + origin.id, + topic.id, + ), user: options.user, channel: channel.id, origin: origin.id, diff --git a/plugins/notifications-common/report.api.md b/plugins/notifications-common/report.api.md index 93889b206b..9ea2931a8d 100644 --- a/plugins/notifications-common/report.api.md +++ b/plugins/notifications-common/report.api.md @@ -11,6 +11,14 @@ export type ChannelSetting = { origins: OriginSetting[]; }; +// @public (undocumented) +export const generateSettingsHash: ( + user: string, + channel: string, + origin: string, + topic: string | null, +) => string; + // @public (undocumented) export const getProcessorFiltersFromConfig: ( config: Config, diff --git a/plugins/notifications-common/src/utils.ts b/plugins/notifications-common/src/utils.ts index b95df5bea5..8017e8fd7c 100644 --- a/plugins/notifications-common/src/utils.ts +++ b/plugins/notifications-common/src/utils.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import { NotificationSettings } from './types'; +import crypto from 'crypto'; /** @public */ export const isNotificationsEnabledFor = ( @@ -40,3 +41,14 @@ export const isNotificationsEnabledFor = ( } return topic.enabled; }; + +/** @public */ +export const generateSettingsHash = ( + user: string, + channel: string, + origin: string, + topic: string | null, +): string => { + const rawKey = `${user}|${channel}|${origin}|${topic ?? ''}`; + return crypto.createHash('sha256').update(rawKey).digest('hex'); +};