diff --git a/integration-tests/modules/__tests__/link-modules/define-link.spec.ts b/integration-tests/modules/__tests__/link-modules/define-link.spec.ts index fc87b8c4c0..651f4756c6 100644 --- a/integration-tests/modules/__tests__/link-modules/define-link.spec.ts +++ b/integration-tests/modules/__tests__/link-modules/define-link.spec.ts @@ -1,8 +1,9 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" + import CurrencyModule from "@medusajs/currency" +import { MedusaModule } from "@medusajs/modules-sdk" import RegionModule from "@medusajs/region" import { defineLink } from "@medusajs/utils" -import { MedusaModule } from "@medusajs/modules-sdk" jest.setTimeout(50000) @@ -46,6 +47,7 @@ medusaIntegrationTestRunner({ args: { methodSuffix: "Currencies", }, + deleteCascade: false, }, { serviceName: "region", @@ -55,6 +57,94 @@ medusaIntegrationTestRunner({ args: { methodSuffix: "Regions", }, + deleteCascade: false, + }, + ], + extends: [ + { + serviceName: "currency", + fieldAlias: { + region: "region_link.region", + }, + relationship: { + serviceName: "currencyCurrencyRegionRegionLink", + primaryKey: "currency_code", + foreignKey: "code", + alias: "region_link", + isList: false, + }, + }, + { + serviceName: "region", + fieldAlias: { + currency: "currency_link.currency", + }, + relationship: { + serviceName: "currencyCurrencyRegionRegionLink", + primaryKey: "region_id", + foreignKey: "id", + alias: "currency_link", + isList: false, + }, + }, + ], + }) + }) + + it("should flag deleteCascade in the link definition", async () => { + const currencyLinks = CurrencyModule.linkable + const regionLinks = RegionModule.linkable + + const link = defineLink( + { + linkable: currencyLinks.currency, + deleteCascade: true, + }, + regionLinks.region + ) + + const linkDefinition = MedusaModule.getCustomLinks() + .map((linkDefinition: any) => { + const definition = linkDefinition( + MedusaModule.getAllJoinerConfigs() + ) + return definition.serviceName === link.serviceName && definition + }) + .filter(Boolean)[0] + + expect(link.serviceName).toEqual("currencyCurrencyRegionRegionLink") + expect(linkDefinition).toEqual({ + serviceName: "currencyCurrencyRegionRegionLink", + isLink: true, + alias: [ + { + name: ["currency_region"], + args: { + entity: "LinkCurrencyCurrencyRegionRegion", + }, + }, + ], + primaryKeys: ["id", "currency_code", "region_id"], + relationships: [ + { + serviceName: "currency", + primaryKey: "code", + foreignKey: "currency_code", + alias: "currency", + args: { + methodSuffix: "Currencies", + }, + deleteCascade: true, + }, + { + serviceName: "region", + primaryKey: "id", + foreignKey: "region_id", + alias: "region", + args: { + methodSuffix: "Regions", + }, + deleteCascade: false, }, ], extends: [ @@ -128,6 +218,7 @@ medusaIntegrationTestRunner({ args: { methodSuffix: "Currencies", }, + deleteCascade: false, }, { serviceName: "region", @@ -137,6 +228,7 @@ medusaIntegrationTestRunner({ args: { methodSuffix: "Regions", }, + deleteCascade: false, }, ], extends: [ diff --git a/packages/core/types/src/common/common.ts b/packages/core/types/src/common/common.ts index ce710376fe..e8ba63edf1 100644 --- a/packages/core/types/src/common/common.ts +++ b/packages/core/types/src/common/common.ts @@ -353,6 +353,18 @@ export type Pluralize = Singular extends `${infer R}ey` | `${infer R}z` | `${infer R}o` ? `${Singular}es` + : Singular extends `${infer R}fe` + ? `${R}ves` + : Singular extends `${infer R}ex` | `${infer R}ix` + ? `${R}ices` + : Singular extends `${infer R}eau` + ? `${R}eaux` + : Singular extends `${infer R}ieu` + ? `${R}ieux` + : Singular extends `${infer R}sis` + ? `${R}ses` + : Singular extends `${infer R}is` + ? `${R}ises` : `${Singular}s` export type SnakeCase = diff --git a/packages/core/utils/package.json b/packages/core/utils/package.json index 37a0ed9a20..f1d3b5ef08 100644 --- a/packages/core/utils/package.json +++ b/packages/core/utils/package.json @@ -40,6 +40,7 @@ "dotenv": "^16.4.5", "jsonwebtoken": "^9.0.2", "knex": "2.4.2", + "pluralize": "^8.0.0", "ulid": "^2.3.0" }, "scripts": { diff --git a/packages/core/utils/src/common/plurailze.ts b/packages/core/utils/src/common/plurailze.ts index f35fb2d89b..e1e5b99d96 100644 --- a/packages/core/utils/src/common/plurailze.ts +++ b/packages/core/utils/src/common/plurailze.ts @@ -1,27 +1,10 @@ +import pluralizeEN from "pluralize" + /** - * Some library provide pluralize function with language specific rules. - * This is a simple implementation of pluralize function. + * Function to pluralize English words. * @param word */ export function pluralize(word: string): string { - // Add basic rules for forming plurals - if ( - //word.endsWith("s") || - word.endsWith("sh") || - word.endsWith("ss") || - word.endsWith("ch") || - word.endsWith("x") || - word.endsWith("o") || - word.endsWith("z") - ) { - return word + "es" - } else if (word.endsWith("y") && !"aeiou".includes(word[word.length - 2])) { - return word.slice(0, -1) + "ies" - } else if (word.endsWith("es")) { - return word - } else if (word.endsWith("fe")) { - return word.slice(0, -2) + "ves" - } else { - return word + "s" - } + // TODO: Implement language specific pluralize function + return pluralizeEN(word) } diff --git a/packages/core/utils/src/modules-sdk/define-link.ts b/packages/core/utils/src/modules-sdk/define-link.ts index 549f8fcf39..4a48e92eb8 100644 --- a/packages/core/utils/src/modules-sdk/define-link.ts +++ b/packages/core/utils/src/modules-sdk/define-link.ts @@ -1,5 +1,5 @@ import { LinkModulesExtraFields, ModuleJoinerConfig } from "@medusajs/types" -import { isObject, pluralize, toPascalCase } from "../common" +import { camelToSnakeCase, isObject, pluralize, toPascalCase } from "../common" import { composeLinkName } from "../link" export const DefineLinkSymbol = Symbol.for("DefineLink") @@ -25,6 +25,7 @@ type CombinedSource = Record & InputToJson type InputOptions = { linkable: CombinedSource | InputSource isList?: boolean + deleteCascade?: boolean } type ExtraOptions = { @@ -44,6 +45,7 @@ type ModuleLinkableKeyConfig = { module: string key: string isList?: boolean + deleteCascade?: boolean primaryKey: string alias: string shortcuts?: { @@ -52,15 +54,15 @@ type ModuleLinkableKeyConfig = { } function isInputOptions(input: any): input is InputOptions { - return isObject(input) && "linkable" in input + return isObject(input) && input?.["linkable"] } function isInputSource(input: any): input is InputSource { - return (isObject(input) && "serviceName" in input) || "toJSON" in input + return (isObject(input) && input?.["serviceName"]) || input?.["toJSON"] } function isToJSON(input: any): input is InputToJson { - return isObject(input) && "toJSON" in input + return isObject(input) && input?.["toJSON"] } export function defineLink( @@ -79,6 +81,7 @@ export function defineLink( alias: source.field, primaryKey: source.primaryKey, isList: false, + deleteCascade: false, module: source.serviceName, } } else if (isInputOptions(leftService)) { @@ -91,6 +94,7 @@ export function defineLink( alias: source.field, primaryKey: source.primaryKey, isList: leftService.isList ?? false, + deleteCascade: leftService.deleteCascade ?? false, module: source.serviceName, } } else { @@ -102,9 +106,10 @@ export function defineLink( serviceBObj = { key: source.linkable, - alias: source.field, + alias: camelToSnakeCase(source.field), primaryKey: source.primaryKey, isList: false, + deleteCascade: false, module: source.serviceName, } } else if (isInputOptions(rightService)) { @@ -114,9 +119,10 @@ export function defineLink( serviceBObj = { key: source.linkable, - alias: source.field, + alias: camelToSnakeCase(source.field), primaryKey: source.primaryKey, isList: rightService.isList ?? false, + deleteCascade: rightService.deleteCascade ?? false, module: source.serviceName, } } else { @@ -281,6 +287,7 @@ export function defineLink( args: { methodSuffix: serviceAMethodSuffix, }, + deleteCascade: serviceAObj.deleteCascade, }, { serviceName: serviceBObj.module, @@ -290,6 +297,7 @@ export function defineLink( args: { methodSuffix: serviceBMethodSuffix, }, + deleteCascade: serviceBObj.deleteCascade, }, ], extends: [ @@ -297,13 +305,13 @@ export function defineLink( serviceName: serviceAObj.module, fieldAlias: { [serviceBObj.isList ? pluralize(aliasB) : aliasB]: - aliasB + "_link." + aliasB, //plural aliasA + aliasB + "_link." + aliasB, }, relationship: { serviceName: output.serviceName, primaryKey: serviceAObj.key, foreignKey: serviceAPrimaryKey, - alias: aliasB + "_link", // plural alias + alias: aliasB + "_link", isList: serviceBObj.isList, }, }, @@ -317,7 +325,7 @@ export function defineLink( serviceName: output.serviceName, primaryKey: serviceBObj.key, foreignKey: serviceBPrimaryKey, - alias: aliasA + "_link", // plural alias + alias: aliasA + "_link", isList: serviceAObj.isList, }, }, diff --git a/yarn.lock b/yarn.lock index 0ca600cd4f..daa8c9a928 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5330,6 +5330,7 @@ __metadata: jest: ^29.7.0 jsonwebtoken: ^9.0.2 knex: 2.4.2 + pluralize: ^8.0.0 rimraf: ^5.0.1 ts-jest: ^29.1.1 typescript: ^5.1.6