feat(utils): define read only link (#9254)
This commit is contained in:
1 parent
6937d74252
commit
b4b1a48987
2 files changed
+162
-31
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)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user