Skip to content

Commit d5a4568

Browse files
committed
fix(core): Let deduplicateTranslations accept an array of translation tables
1 parent e26ed7b commit d5a4568

1 file changed

Lines changed: 39 additions & 20 deletions

File tree

packages/core/src/migration-utils/translation-deduplication.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,50 @@ import { QueryRunner } from 'typeorm';
33

44
/**
55
* @description
6-
* Removes duplicate rows from a translation table, keeping only the most recently updated
7-
* row per `(baseId, languageCode)` pair.
6+
* Removes duplicate rows from one or more translation tables, keeping only the most recently
7+
* updated row per `(baseId, languageCode)` pair.
88
*
99
* Prior to the fix for a race condition in `TranslatableSaver.update()`, two concurrent
1010
* updates could both add a translation for the same language before either had committed,
1111
* resulting in two rows for the same `(baseId, languageCode)` pair. This is no longer
1212
* possible going forward, but a unique constraint on translation tables cannot be added to an
1313
* existing database that already contains such duplicates without first removing them.
1414
*
15+
* This applies to any table backing a `Translation<T>` entity used with `TranslatableSaver`,
16+
* including translation tables defined by plugins for their own custom translatable entities —
17+
* not just the 13 core tables shown below.
18+
*
1519
* Call this from your migration's `up()` method **before** the unique constraint on the
16-
* translation table is created, once per affected table. `vendure migrate generate` will
17-
* generate the DDL that creates the constraints; you only need to add the
18-
* `deduplicateTranslations` calls ahead of it. On Postgres this looks like (constraint names
19-
* are deterministic, so `vendure migrate generate` will produce these exact names):
20+
* translation table(s) is created. `vendure migrate generate` will generate the DDL that
21+
* creates the constraints; you only need to add a `deduplicateTranslations` call ahead of it,
22+
* passing either a single table name or an array of table names. On Postgres this looks like
23+
* (constraint names are deterministic, so `vendure migrate generate` will produce these exact
24+
* names):
2025
*
2126
* ```ts
2227
* import { MigrationInterface, QueryRunner } from 'typeorm';
2328
* import { deduplicateTranslations } from '\@vendure/core';
2429
*
2530
* export class AddTranslationUniqueConstraints1234567890 implements MigrationInterface {
2631
* public async up(queryRunner: QueryRunner): Promise<any> {
27-
* // --- Remove any pre-existing duplicate rows, one call per translation table ---
28-
* await deduplicateTranslations(queryRunner, 'product_translation');
29-
* await deduplicateTranslations(queryRunner, 'product_variant_translation');
30-
* await deduplicateTranslations(queryRunner, 'product_option_translation');
31-
* await deduplicateTranslations(queryRunner, 'product_option_group_translation');
32-
* await deduplicateTranslations(queryRunner, 'collection_translation');
33-
* await deduplicateTranslations(queryRunner, 'facet_translation');
34-
* await deduplicateTranslations(queryRunner, 'facet_value_translation');
35-
* await deduplicateTranslations(queryRunner, 'asset_translation');
36-
* await deduplicateTranslations(queryRunner, 'api_key_translation');
37-
* await deduplicateTranslations(queryRunner, 'payment_method_translation');
38-
* await deduplicateTranslations(queryRunner, 'promotion_translation');
39-
* await deduplicateTranslations(queryRunner, 'region_translation');
40-
* await deduplicateTranslations(queryRunner, 'shipping_method_translation');
32+
* // --- Remove any pre-existing duplicate rows across all core translation tables ---
33+
* await deduplicateTranslations(queryRunner, [
34+
* 'product_translation',
35+
* 'product_variant_translation',
36+
* 'product_option_translation',
37+
* 'product_option_group_translation',
38+
* 'collection_translation',
39+
* 'facet_translation',
40+
* 'facet_value_translation',
41+
* 'asset_translation',
42+
* 'api_key_translation',
43+
* 'payment_method_translation',
44+
* 'promotion_translation',
45+
* 'region_translation',
46+
* 'shipping_method_translation',
47+
* // Include any of your own custom translatable entities' tables here too, e.g.:
48+
* // 'my_custom_entity_translation',
49+
* ]);
4150
*
4251
* // --- Auto-generated DDL continues (Postgres) ---
4352
* await queryRunner.query(`ALTER TABLE "product_translation" ADD CONSTRAINT "UQ_dcc35f0d2b8d422634e878b813c" UNIQUE ("languageCode", "baseId")`);
@@ -72,6 +81,16 @@ import { QueryRunner } from 'typeorm';
7281
* @docsCategory migration
7382
*/
7483
export async function deduplicateTranslations(
84+
queryRunner: QueryRunner,
85+
translationTableNames: string | string[],
86+
): Promise<void> {
87+
const tableNames = Array.isArray(translationTableNames) ? translationTableNames : [translationTableNames];
88+
for (const tableName of tableNames) {
89+
await deduplicateTranslationTable(queryRunner, tableName);
90+
}
91+
}
92+
93+
async function deduplicateTranslationTable(
7594
queryRunner: QueryRunner,
7695
translationTableName: string,
7796
): Promise<void> {

0 commit comments

Comments
 (0)