feat(utils): define read only link (#9254)

This commit is contained in:
Carlos R. L. Rodrigues authored and GitHub committed 2024-09-24 12:00:38 -03:00
1 parent 6937d74252
commit b4b1a48987
2 files changed
+166 -35

No files matched your search

@@ -76,6 +76,7 @@ medusaIntegrationTestRunner({
}, },
relationship: { relationship: {
serviceName: "CurrencyCurrencyRegionRegionLink", serviceName: "CurrencyCurrencyRegionRegionLink",
entity: "LinkCurrencyCurrencyRegionRegion",
primaryKey: "currency_code", primaryKey: "currency_code",
foreignKey: "code", foreignKey: "code",
alias: "region_link", alias: "region_link",
@@ -93,6 +94,7 @@ medusaIntegrationTestRunner({
}, },
relationship: { relationship: {
serviceName: "CurrencyCurrencyRegionRegionLink", serviceName: "CurrencyCurrencyRegionRegionLink",
entity: "LinkCurrencyCurrencyRegionRegion",
primaryKey: "region_id", primaryKey: "region_id",
foreignKey: "id", foreignKey: "id",
alias: "currency_link", alias: "currency_link",
@@ -171,6 +173,7 @@ medusaIntegrationTestRunner({
}, },
relationship: { relationship: {
serviceName: "ProductProductVariantRegionRegionLink", serviceName: "ProductProductVariantRegionRegionLink",
entity: "LinkProductProductVariantRegionRegion",
primaryKey: "product_variant_id", primaryKey: "product_variant_id",
foreignKey: "id", foreignKey: "id",
alias: "region_link", alias: "region_link",
@@ -190,6 +193,7 @@ medusaIntegrationTestRunner({
}, },
relationship: { relationship: {
serviceName: "ProductProductVariantRegionRegionLink", serviceName: "ProductProductVariantRegionRegionLink",
entity: "LinkProductProductVariantRegionRegion",
primaryKey: "region_id", primaryKey: "region_id",
foreignKey: "id", foreignKey: "id",
alias: "product_variant_link", alias: "product_variant_link",
@@ -271,6 +275,7 @@ medusaIntegrationTestRunner({
}, },
relationship: { relationship: {
serviceName: "CurrencyCurrencyRegionRegionLink", serviceName: "CurrencyCurrencyRegionRegionLink",
entity: "LinkCurrencyCurrencyRegionRegion",
primaryKey: "currency_code", primaryKey: "currency_code",
foreignKey: "code", foreignKey: "code",
alias: "region_link", alias: "region_link",
@@ -288,6 +293,7 @@ medusaIntegrationTestRunner({
}, },
relationship: { relationship: {
serviceName: "CurrencyCurrencyRegionRegionLink", serviceName: "CurrencyCurrencyRegionRegionLink",
entity: "LinkCurrencyCurrencyRegionRegion",
primaryKey: "region_id", primaryKey: "region_id",
foreignKey: "id", foreignKey: "id",
alias: "currency_link", alias: "currency_link",
@@ -365,6 +371,7 @@ medusaIntegrationTestRunner({
}, },
relationship: { relationship: {
serviceName: "CurrencyCurrencyRegionRegionLink", serviceName: "CurrencyCurrencyRegionRegionLink",
entity: "LinkCurrencyCurrencyRegionRegion",
primaryKey: "currency_code", primaryKey: "currency_code",
foreignKey: "code", foreignKey: "code",
alias: "region_link", alias: "region_link",
@@ -382,6 +389,7 @@ medusaIntegrationTestRunner({
}, },
relationship: { relationship: {
serviceName: "CurrencyCurrencyRegionRegionLink", serviceName: "CurrencyCurrencyRegionRegionLink",
entity: "LinkCurrencyCurrencyRegionRegion",
primaryKey: "region_id", primaryKey: "region_id",
foreignKey: "id", foreignKey: "id",
alias: "currency_link", alias: "currency_link",
@@ -7,6 +7,7 @@ export const DefineLinkSymbol = Symbol.for("DefineLink")
export interface DefineLinkExport { export interface DefineLinkExport {
[DefineLinkSymbol]: boolean [DefineLinkSymbol]: boolean
serviceName: string serviceName: string
entity?: string
entryPoint: string entryPoint: string
} }
@@ -19,6 +20,17 @@ type InputSource = {
primaryKey: string primaryKey: string
} }
type ReadOnlyInputSource = {
linkable:
| CombinedSource
| InputSource
| {
serviceName: string
entity?: string
}
field?: string
}
type InputToJson = { type InputToJson = {
toJSON: () => InputSource toJSON: () => InputSource
} }
@@ -27,10 +39,18 @@ type CombinedSource = Record<any, any> & InputToJson
type InputOptions = { type InputOptions = {
linkable: CombinedSource | InputSource linkable: CombinedSource | InputSource
field?: string
isList?: boolean isList?: boolean
deleteCascade?: boolean deleteCascade?: boolean
} }
type Shortcut = {
property: string
path: string
isList?: boolean
forwardArguments?: string | string[]
}
type ExtraOptions = { type ExtraOptions = {
pk?: { pk?: {
[key: string]: string [key: string]: string
@@ -40,10 +60,22 @@ type ExtraOptions = {
idPrefix?: string idPrefix?: string
extraColumns?: LinkModulesExtraFields extraColumns?: LinkModulesExtraFields
} }
readOnly?: boolean
}
type ReadOnlyExtraOptions = {
readOnly: true
isList?: boolean
shortcut?: Shortcut | Shortcut[]
} }
type DefineLinkInputSource = InputSource | InputOptions | CombinedSource type DefineLinkInputSource = InputSource | InputOptions | CombinedSource
type DefineReadOnlyLinkInputSource =
| ReadOnlyInputSource
| InputOptions
| CombinedSource
type ModuleLinkableKeyConfig = { type ModuleLinkableKeyConfig = {
module: string module: string
entity?: string entity?: string
@@ -53,9 +85,7 @@ type ModuleLinkableKeyConfig = {
deleteCascade?: boolean deleteCascade?: boolean
primaryKey: string primaryKey: string
alias: string alias: string
shortcuts?: { shortcut?: Shortcut | Shortcut[]
[key: string]: string | { path: string; isList?: boolean }
}
} }
function isInputOptions(input: any): input is InputOptions { function isInputOptions(input: any): input is InputOptions {
@@ -70,7 +100,33 @@ function isToJSON(input: any): input is InputToJson {
return isObject(input) && input?.["toJSON"] return isObject(input) && input?.["toJSON"]
} }
function prepareServiceConfig(input: DefineLinkInputSource) { function buildFieldAlias(fieldAliases?: Shortcut | Shortcut[]) {
if (!fieldAliases) {
return
}
const fieldAlias = {}
const shortcuts = Array.isArray(fieldAliases) ? fieldAliases : [fieldAliases]
for (const sc of shortcuts) {
const fwArgs = sc.forwardArguments
? Array.isArray(sc.forwardArguments)
? sc.forwardArguments
: [sc.forwardArguments]
: []
fieldAlias[sc.property] = {
path: sc.path,
isList: !!sc.isList,
forwardArgumentsOnPath: fwArgs,
}
}
return fieldAlias
}
function prepareServiceConfig(
input: DefineLinkInputSource | DefineReadOnlyLinkInputSource
) {
let serviceConfig = {} as ModuleLinkableKeyConfig let serviceConfig = {} as ModuleLinkableKeyConfig
if (isInputSource(input)) { if (isInputSource(input)) {
@@ -78,8 +134,8 @@ function prepareServiceConfig(input: DefineLinkInputSource) {
serviceConfig = { serviceConfig = {
key: source.linkable, key: source.linkable,
alias: source.alias ?? camelToSnakeCase(source.field), alias: source.alias ?? camelToSnakeCase(source.field ?? ""),
field: source.field, field: input.field ?? source.field,
primaryKey: source.primaryKey, primaryKey: source.primaryKey,
isList: false, isList: false,
deleteCascade: false, deleteCascade: false,
@@ -93,8 +149,8 @@ function prepareServiceConfig(input: DefineLinkInputSource) {
serviceConfig = { serviceConfig = {
key: source.linkable, key: source.linkable,
alias: source.alias ?? camelToSnakeCase(source.field), alias: source.alias ?? camelToSnakeCase(source.field ?? ""),
field: source.field, field: input.field ?? source.field,
primaryKey: source.primaryKey, primaryKey: source.primaryKey,
isList: input.isList ?? false, isList: input.isList ?? false,
deleteCascade: input.deleteCascade ?? false, deleteCascade: input.deleteCascade ?? false,
@@ -123,14 +179,27 @@ function prepareServiceConfig(input: DefineLinkInputSource) {
* @param linkServiceOptions * @param linkServiceOptions
*/ */
export function defineLink( export function defineLink(
leftService: DefineLinkInputSource, leftService: DefineLinkInputSource | DefineReadOnlyLinkInputSource,
rightService: DefineLinkInputSource, rightService: DefineLinkInputSource | DefineReadOnlyLinkInputSource,
linkServiceOptions?: ExtraOptions linkServiceOptions?: ExtraOptions | ReadOnlyExtraOptions
): DefineLinkExport { ): DefineLinkExport {
const serviceAObj = prepareServiceConfig(leftService) const serviceAObj = prepareServiceConfig(leftService)
const serviceBObj = prepareServiceConfig(rightService) const serviceBObj = prepareServiceConfig(rightService)
const output = { [DefineLinkSymbol]: true, serviceName: "", entryPoint: "" } if (linkServiceOptions?.readOnly) {
return defineReadOnlyLink(
serviceAObj,
serviceBObj,
linkServiceOptions as ReadOnlyExtraOptions
) as unknown as DefineLinkExport
}
const output = {
[DefineLinkSymbol]: true,
serviceName: "",
entity: "",
entryPoint: "",
}
const register = function ( const register = function (
modules: ModuleJoinerConfig[] modules: ModuleJoinerConfig[]
@@ -276,6 +345,9 @@ ${serviceBObj.module}: {
) )
output.entryPoint = aliasA + "_" + aliasB output.entryPoint = aliasA + "_" + aliasB
output.entity = toPascalCase(
["Link", serviceAObj.module, aliasA, serviceBObj.module, aliasB].join("_")
)
const linkDefinition: ModuleJoinerConfig = { const linkDefinition: ModuleJoinerConfig = {
serviceName: output.serviceName, serviceName: output.serviceName,
@@ -284,15 +356,7 @@ ${serviceBObj.module}: {
{ {
name: [output.entryPoint], name: [output.entryPoint],
args: { args: {
entity: toPascalCase( entity: output.entity,
[
"Link",
serviceAObj.module,
aliasA,
serviceBObj.module,
aliasB,
].join("_")
),
}, },
}, },
], ],
@@ -324,15 +388,15 @@ ${serviceBObj.module}: {
extends: [ extends: [
{ {
serviceName: serviceAObj.module, serviceName: serviceAObj.module,
fieldAlias: { fieldAlias: buildFieldAlias({
[serviceBObj.isList ? pluralize(aliasB) : aliasB]: { property: serviceBObj.isList ? pluralize(aliasB) : aliasB,
path: aliasB + "_link." + aliasB, path: aliasB + "_link." + aliasB,
isList: serviceBObj.isList, isList: serviceBObj.isList,
forwardArgumentsOnPath: [aliasB + "_link." + aliasB], forwardArguments: [aliasB + "_link." + aliasB],
}, }),
},
relationship: { relationship: {
serviceName: output.serviceName, serviceName: output.serviceName,
entity: output.entity,
primaryKey: serviceAObj.key, primaryKey: serviceAObj.key,
foreignKey: serviceAPrimaryKey, foreignKey: serviceAPrimaryKey,
alias: aliasB + "_link", alias: aliasB + "_link",
@@ -341,15 +405,15 @@ ${serviceBObj.module}: {
}, },
{ {
serviceName: serviceBObj.module, serviceName: serviceBObj.module,
fieldAlias: { fieldAlias: buildFieldAlias({
[serviceAObj.isList ? pluralize(aliasA) : aliasA]: { property: serviceAObj.isList ? pluralize(aliasA) : aliasA,
path: aliasA + "_link." + aliasA, path: aliasA + "_link." + aliasA,
isList: serviceAObj.isList, isList: serviceAObj.isList,
forwardArgumentsOnPath: [aliasA + "_link." + aliasA], forwardArguments: [aliasA + "_link." + aliasA],
}, }),
},
relationship: { relationship: {
serviceName: output.serviceName, serviceName: output.serviceName,
entity: output.entity,
primaryKey: serviceBObj.key, primaryKey: serviceBObj.key,
foreignKey: serviceBPrimaryKey, foreignKey: serviceBPrimaryKey,
alias: aliasA + "_link", alias: aliasA + "_link",
@@ -375,3 +439,62 @@ ${serviceBObj.module}: {
return output return output
} }
function defineReadOnlyLink(
serviceAObj: ModuleLinkableKeyConfig,
serviceBObj: ModuleLinkableKeyConfig,
readOnlyLinkOptions?: ReadOnlyExtraOptions
): void {
const register = function (
modules: ModuleJoinerConfig[]
): ModuleJoinerConfig {
const serviceAInfo = modules.find(
(mod) => mod.serviceName === serviceAObj.module
)!
const serviceBInfo = modules.find(
(mod) => mod.serviceName === serviceBObj.module
)!
if (!serviceAInfo) {
throw new Error(`Service ${serviceAObj.module} was not found. If this is your module, make sure you set isQueryable to true in medusa-config.js:
${serviceAObj.module}: {
// ...
definition: {
isQueryable: true
}
}`)
}
if (!serviceBInfo) {
throw new Error(`Service ${serviceBObj.module} was not found. If this is your module, make sure you set isQueryable to true in medusa-config.js:
${serviceBObj.module}: {
// ...
definition: {
isQueryable: true
}
}`)
}
return {
isLink: true,
isReadOnlyLink: true,
extends: [
{
serviceName: serviceAObj.module,
fieldAlias: buildFieldAlias(readOnlyLinkOptions?.shortcut),
relationship: {
serviceName: serviceBObj.module,
entity: serviceBObj.entity,
primaryKey: serviceBObj.primaryKey,
foreignKey: serviceAObj.field,
alias: serviceBObj.alias,
isList: readOnlyLinkOptions?.isList ?? serviceAObj.isList,
},
},
],
}
}
global.MedusaModule.setCustomLink(register)
}