feat(modules-sdk): define link (#7743)
This commit is contained in:
committed by
GitHub
parent
0886869148
commit
bc0c65c6b3
@@ -6,3 +6,4 @@ export * from "./medusa-module"
|
||||
export * from "./remote-link"
|
||||
export * from "./remote-query"
|
||||
export * from "./types"
|
||||
export * from "./utils/define-link"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Knex } from "knex"
|
||||
import { mergeTypeDefs } from "@graphql-tools/merge"
|
||||
import { makeExecutableSchema } from "@graphql-tools/schema"
|
||||
import { RemoteFetchDataCallback } from "@medusajs/orchestration"
|
||||
@@ -26,12 +25,13 @@ import {
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import { asValue } from "awilix"
|
||||
import type { Knex } from "knex"
|
||||
import {
|
||||
MODULE_PACKAGE_NAMES,
|
||||
ModuleRegistrationName,
|
||||
Modules,
|
||||
} from "./definitions"
|
||||
import { MedusaModule } from "./medusa-module"
|
||||
import { MedusaModule, RegisterModuleJoinerConfig } from "./medusa-module"
|
||||
import { RemoteLink } from "./remote-link"
|
||||
import { RemoteQuery } from "./remote-query"
|
||||
import { MODULE_RESOURCE_TYPE, MODULE_SCOPE } from "./types"
|
||||
@@ -240,7 +240,7 @@ export type MedusaAppOptions = {
|
||||
modulesConfigPath?: string
|
||||
modulesConfigFileName?: string
|
||||
modulesConfig?: MedusaModuleConfig
|
||||
linkModules?: ModuleJoinerConfig | ModuleJoinerConfig[]
|
||||
linkModules?: RegisterModuleJoinerConfig | RegisterModuleJoinerConfig[]
|
||||
remoteFetchData?: RemoteFetchDataCallback
|
||||
injectedDependencies?: any
|
||||
onApplicationStartCb?: () => void
|
||||
@@ -260,7 +260,6 @@ async function MedusaApp_({
|
||||
linkModules,
|
||||
remoteFetchData,
|
||||
injectedDependencies = {},
|
||||
onApplicationStartCb,
|
||||
migrationOnly = false,
|
||||
loaderOnly = false,
|
||||
workerMode = "server",
|
||||
@@ -363,6 +362,20 @@ async function MedusaApp_({
|
||||
allowUnregistered: true,
|
||||
})
|
||||
|
||||
linkModules ??= []
|
||||
if (!Array.isArray(linkModules)) {
|
||||
linkModules = [linkModules]
|
||||
}
|
||||
linkModules.push(...MedusaModule.getCustomLinks())
|
||||
|
||||
const allLoadedJoinerConfigs = MedusaModule.getAllJoinerConfigs()
|
||||
for (let linkIdx = 0; linkIdx < linkModules.length; linkIdx++) {
|
||||
const customLink: any = linkModules[linkIdx]
|
||||
if (typeof customLink === "function") {
|
||||
linkModules[linkIdx] = customLink(allLoadedJoinerConfigs)
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
remoteLink,
|
||||
runMigrations: linkModuleMigration,
|
||||
|
||||
@@ -80,10 +80,15 @@ export type LinkModuleBootstrapOptions = {
|
||||
injectedDependencies?: Record<string, any>
|
||||
}
|
||||
|
||||
export type RegisterModuleJoinerConfig =
|
||||
| ModuleJoinerConfig
|
||||
| ((modules: ModuleJoinerConfig[]) => ModuleJoinerConfig)
|
||||
|
||||
export class MedusaModule {
|
||||
private static instances_: Map<string, { [key: string]: IModuleService }> =
|
||||
new Map()
|
||||
private static modules_: Map<string, ModuleAlias[]> = new Map()
|
||||
private static customLinks_: RegisterModuleJoinerConfig[] = []
|
||||
private static loading_: Map<string, Promise<any>> = new Map()
|
||||
private static joinerConfig_: Map<string, ModuleJoinerConfig> = new Map()
|
||||
private static moduleResolutions_: Map<string, ModuleResolution> = new Map()
|
||||
@@ -203,6 +208,14 @@ export class MedusaModule {
|
||||
return config
|
||||
}
|
||||
|
||||
public static setCustomLink(config: RegisterModuleJoinerConfig): void {
|
||||
MedusaModule.customLinks_.push(config)
|
||||
}
|
||||
|
||||
public static getCustomLinks(): RegisterModuleJoinerConfig[] {
|
||||
return MedusaModule.customLinks_
|
||||
}
|
||||
|
||||
public static getModuleInstance(
|
||||
moduleKey: string,
|
||||
alias?: string
|
||||
|
||||
232
packages/core/modules-sdk/src/utils/define-link.ts
Normal file
232
packages/core/modules-sdk/src/utils/define-link.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import { LinkModulesExtraFields, ModuleJoinerConfig } from "@medusajs/types"
|
||||
import {
|
||||
composeLinkName,
|
||||
isObject,
|
||||
isString,
|
||||
toPascalCase,
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaModule } from "../medusa-module"
|
||||
|
||||
type ModuleLinkableKeyConfig = {
|
||||
module: string
|
||||
key: string
|
||||
isList?: boolean
|
||||
alias?: string
|
||||
shortcuts?: {
|
||||
[key: string]: string | { path: string; isList?: boolean }
|
||||
}
|
||||
}
|
||||
|
||||
export function defineLink(
|
||||
serviceAAndKey: string | ModuleLinkableKeyConfig,
|
||||
serviceBAndKey: string | ModuleLinkableKeyConfig,
|
||||
options?: {
|
||||
pk?: {
|
||||
[key: string]: string
|
||||
}
|
||||
database?: {
|
||||
table: string
|
||||
idPrefix?: string
|
||||
extraColumns?: LinkModulesExtraFields
|
||||
}
|
||||
}
|
||||
) {
|
||||
const register = function (
|
||||
modules: ModuleJoinerConfig[]
|
||||
): ModuleJoinerConfig {
|
||||
let serviceA: string
|
||||
let serviceAKey: string
|
||||
let serviceAIsList = false
|
||||
let serviceAObj: Partial<ModuleLinkableKeyConfig> = {}
|
||||
|
||||
let serviceB: string
|
||||
let serviceBKey: string
|
||||
let serviceBIsList = false
|
||||
let serviceBObj: Partial<ModuleLinkableKeyConfig> = {}
|
||||
|
||||
if (isString(serviceAAndKey)) {
|
||||
let [mod, key] = (serviceAAndKey as string).split(".")
|
||||
serviceA = mod
|
||||
if (key.endsWith("[]")) {
|
||||
serviceAIsList = true
|
||||
key = key.slice(0, -2)
|
||||
}
|
||||
serviceAKey = key
|
||||
} else if (isObject(serviceAAndKey)) {
|
||||
const objA = serviceAAndKey as ModuleLinkableKeyConfig
|
||||
serviceAObj = objA
|
||||
|
||||
serviceA = objA.module
|
||||
serviceAKey = objA.key
|
||||
serviceAIsList = !!objA.isList
|
||||
} else {
|
||||
throw new Error("Invalid value for serviceA config")
|
||||
}
|
||||
|
||||
if (isString(serviceBAndKey)) {
|
||||
let [mod, key] = (serviceBAndKey as string).split(".")
|
||||
serviceB = mod
|
||||
if (key.endsWith("[]")) {
|
||||
serviceBIsList = true
|
||||
key = key.slice(0, -2)
|
||||
}
|
||||
serviceBKey = key
|
||||
} else if (isObject(serviceBAndKey)) {
|
||||
const objB = serviceBAndKey as ModuleLinkableKeyConfig
|
||||
serviceBObj = objB
|
||||
|
||||
serviceB = objB.module
|
||||
serviceBKey = objB.key
|
||||
serviceBIsList = !!objB.isList
|
||||
} else {
|
||||
throw new Error("Invalid value for serviceB config")
|
||||
}
|
||||
|
||||
const serviceAInfo = modules.find((mod) => mod.serviceName === serviceA)
|
||||
const serviceBInfo = modules.find((mod) => mod.serviceName === serviceB)
|
||||
if (!serviceAInfo) {
|
||||
throw new Error(`Service ${serviceA} was not found`)
|
||||
}
|
||||
if (!serviceBInfo) {
|
||||
throw new Error(`Service ${serviceB} was not found`)
|
||||
}
|
||||
|
||||
const serviceAKeyInfo = serviceAInfo.linkableKeys?.[serviceAKey]
|
||||
const serviceBKeyInfo = serviceBInfo.linkableKeys?.[serviceBKey]
|
||||
if (!serviceAKeyInfo) {
|
||||
throw new Error(
|
||||
`Key ${serviceAKey} is not linkable on service ${serviceA}`
|
||||
)
|
||||
}
|
||||
if (!serviceBKeyInfo) {
|
||||
throw new Error(
|
||||
`Key ${serviceBKey} is not linkable on service ${serviceB}`
|
||||
)
|
||||
}
|
||||
|
||||
let serviceAAliases = serviceAInfo.alias ?? []
|
||||
if (!Array.isArray(serviceAAliases)) {
|
||||
serviceAAliases = [serviceAAliases]
|
||||
}
|
||||
|
||||
let aliasAOptions =
|
||||
serviceAObj.alias ??
|
||||
serviceAAliases.find((a) => {
|
||||
return a.args?.entity == serviceAKeyInfo
|
||||
})?.name
|
||||
|
||||
let aliasA = ""
|
||||
if (Array.isArray(aliasAOptions)) {
|
||||
aliasA = aliasAOptions[0]
|
||||
}
|
||||
if (!aliasA) {
|
||||
throw new Error(
|
||||
`You need to provide an alias for ${serviceA}.${serviceAKey}`
|
||||
)
|
||||
}
|
||||
|
||||
let serviceBAliases = serviceBInfo.alias ?? []
|
||||
if (!Array.isArray(serviceBAliases)) {
|
||||
serviceBAliases = [serviceBAliases]
|
||||
}
|
||||
|
||||
let aliasBOptions =
|
||||
serviceBObj.alias ??
|
||||
serviceBAliases.find((a) => {
|
||||
return a.args?.entity == serviceBKeyInfo
|
||||
})?.name
|
||||
|
||||
let aliasB = ""
|
||||
if (Array.isArray(aliasBOptions)) {
|
||||
aliasB = aliasBOptions[0]
|
||||
}
|
||||
if (!aliasB) {
|
||||
throw new Error(
|
||||
`You need to provide an alias for ${serviceB}.${serviceBKey}`
|
||||
)
|
||||
}
|
||||
|
||||
let serviceAPrimaryKey = options?.pk?.[serviceA] ?? serviceAInfo.primaryKeys
|
||||
if (Array.isArray(serviceAPrimaryKey)) {
|
||||
serviceAPrimaryKey = serviceAPrimaryKey[0]
|
||||
}
|
||||
|
||||
let serviceBPrimaryKey = options?.pk?.[serviceB] ?? serviceBInfo.primaryKeys
|
||||
if (Array.isArray(serviceBPrimaryKey)) {
|
||||
serviceBPrimaryKey = serviceBPrimaryKey[0]
|
||||
}
|
||||
|
||||
const serviceName = composeLinkName(serviceA, aliasA, serviceB, aliasB)
|
||||
|
||||
const linkDefinition: ModuleJoinerConfig = {
|
||||
serviceName,
|
||||
isLink: true,
|
||||
alias: [
|
||||
{
|
||||
name: [aliasA + "_" + aliasB],
|
||||
args: {
|
||||
entity: toPascalCase(
|
||||
["Link", serviceA, aliasA, serviceB, aliasB].join("_")
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
primaryKeys: ["id", serviceAKey, serviceBKey],
|
||||
relationships: [
|
||||
{
|
||||
serviceName: serviceA,
|
||||
primaryKey: serviceAPrimaryKey!,
|
||||
foreignKey: serviceAKey,
|
||||
alias: aliasA,
|
||||
},
|
||||
{
|
||||
serviceName: serviceB,
|
||||
primaryKey: serviceBPrimaryKey!,
|
||||
foreignKey: serviceBKey,
|
||||
alias: aliasB,
|
||||
},
|
||||
],
|
||||
extends: [
|
||||
{
|
||||
serviceName: serviceA,
|
||||
fieldAlias: {
|
||||
[aliasB]: aliasB + "_link." + aliasB, //plural aliasA
|
||||
},
|
||||
relationship: {
|
||||
serviceName,
|
||||
primaryKey: serviceAKey,
|
||||
foreignKey: serviceBPrimaryKey!,
|
||||
alias: aliasB + "_link", // plural alias
|
||||
isList: serviceAIsList,
|
||||
},
|
||||
},
|
||||
{
|
||||
serviceName: serviceB,
|
||||
fieldAlias: {
|
||||
[aliasA]: aliasA + "_link." + aliasA,
|
||||
},
|
||||
relationship: {
|
||||
serviceName,
|
||||
primaryKey: serviceBKey,
|
||||
foreignKey: serviceAPrimaryKey!,
|
||||
alias: aliasA + "_link", // plural alias
|
||||
isList: serviceBIsList,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
if (options?.database) {
|
||||
const { table, idPrefix, extraColumns } = options.database
|
||||
linkDefinition.databaseConfig = {
|
||||
tableName: table,
|
||||
idPrefix,
|
||||
extraFields: extraColumns,
|
||||
}
|
||||
}
|
||||
|
||||
return linkDefinition
|
||||
}
|
||||
|
||||
MedusaModule.setCustomLink(register)
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./clean-graphql-schema"
|
||||
export * from "./define-link"
|
||||
export * from "./graphql-schema-to-fields"
|
||||
|
||||
Reference in New Issue
Block a user