feat(utils): define read only link (#9254)
This commit is contained in:
committed by
GitHub
parent
6937d74252
commit
b4b1a48987
@@ -7,6 +7,7 @@ export const DefineLinkSymbol = Symbol.for("DefineLink")
|
||||
export interface DefineLinkExport {
|
||||
[DefineLinkSymbol]: boolean
|
||||
serviceName: string
|
||||
entity?: string
|
||||
entryPoint: string
|
||||
}
|
||||
|
||||
@@ -19,6 +20,17 @@ type InputSource = {
|
||||
primaryKey: string
|
||||
}
|
||||
|
||||
type ReadOnlyInputSource = {
|
||||
linkable:
|
||||
| CombinedSource
|
||||
| InputSource
|
||||
| {
|
||||
serviceName: string
|
||||
entity?: string
|
||||
}
|
||||
field?: string
|
||||
}
|
||||
|
||||
type InputToJson = {
|
||||
toJSON: () => InputSource
|
||||
}
|
||||
@@ -27,10 +39,18 @@ type CombinedSource = Record<any, any> & InputToJson
|
||||
|
||||
type InputOptions = {
|
||||
linkable: CombinedSource | InputSource
|
||||
field?: string
|
||||
isList?: boolean
|
||||
deleteCascade?: boolean
|
||||
}
|
||||
|
||||
type Shortcut = {
|
||||
property: string
|
||||
path: string
|
||||
isList?: boolean
|
||||
forwardArguments?: string | string[]
|
||||
}
|
||||
|
||||
type ExtraOptions = {
|
||||
pk?: {
|
||||
[key: string]: string
|
||||
@@ -40,10 +60,22 @@ type ExtraOptions = {
|
||||
idPrefix?: string
|
||||
extraColumns?: LinkModulesExtraFields
|
||||
}
|
||||
readOnly?: boolean
|
||||
}
|
||||
|
||||
type ReadOnlyExtraOptions = {
|
||||
readOnly: true
|
||||
isList?: boolean
|
||||
shortcut?: Shortcut | Shortcut[]
|
||||
}
|
||||
|
||||
type DefineLinkInputSource = InputSource | InputOptions | CombinedSource
|
||||
|
||||
type DefineReadOnlyLinkInputSource =
|
||||
| ReadOnlyInputSource
|
||||
| InputOptions
|
||||
| CombinedSource
|
||||
|
||||
type ModuleLinkableKeyConfig = {
|
||||
module: string
|
||||
entity?: string
|
||||
@@ -53,9 +85,7 @@ type ModuleLinkableKeyConfig = {
|
||||
deleteCascade?: boolean
|
||||
primaryKey: string
|
||||
alias: string
|
||||
shortcuts?: {
|
||||
[key: string]: string | { path: string; isList?: boolean }
|
||||
}
|
||||
shortcut?: Shortcut | Shortcut[]
|
||||
}
|
||||
|
||||
function isInputOptions(input: any): input is InputOptions {
|
||||
@@ -70,7 +100,33 @@ function isToJSON(input: any): input is InputToJson {
|
||||
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
|
||||
|
||||
if (isInputSource(input)) {
|
||||
@@ -78,8 +134,8 @@ function prepareServiceConfig(input: DefineLinkInputSource) {
|
||||
|
||||
serviceConfig = {
|
||||
key: source.linkable,
|
||||
alias: source.alias ?? camelToSnakeCase(source.field),
|
||||
field: source.field,
|
||||
alias: source.alias ?? camelToSnakeCase(source.field ?? ""),
|
||||
field: input.field ?? source.field,
|
||||
primaryKey: source.primaryKey,
|
||||
isList: false,
|
||||
deleteCascade: false,
|
||||
@@ -93,8 +149,8 @@ function prepareServiceConfig(input: DefineLinkInputSource) {
|
||||
|
||||
serviceConfig = {
|
||||
key: source.linkable,
|
||||
alias: source.alias ?? camelToSnakeCase(source.field),
|
||||
field: source.field,
|
||||
alias: source.alias ?? camelToSnakeCase(source.field ?? ""),
|
||||
field: input.field ?? source.field,
|
||||
primaryKey: source.primaryKey,
|
||||
isList: input.isList ?? false,
|
||||
deleteCascade: input.deleteCascade ?? false,
|
||||
@@ -123,14 +179,27 @@ function prepareServiceConfig(input: DefineLinkInputSource) {
|
||||
* @param linkServiceOptions
|
||||
*/
|
||||
export function defineLink(
|
||||
leftService: DefineLinkInputSource,
|
||||
rightService: DefineLinkInputSource,
|
||||
linkServiceOptions?: ExtraOptions
|
||||
leftService: DefineLinkInputSource | DefineReadOnlyLinkInputSource,
|
||||
rightService: DefineLinkInputSource | DefineReadOnlyLinkInputSource,
|
||||
linkServiceOptions?: ExtraOptions | ReadOnlyExtraOptions
|
||||
): DefineLinkExport {
|
||||
const serviceAObj = prepareServiceConfig(leftService)
|
||||
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 (
|
||||
modules: ModuleJoinerConfig[]
|
||||
@@ -276,6 +345,9 @@ ${serviceBObj.module}: {
|
||||
)
|
||||
|
||||
output.entryPoint = aliasA + "_" + aliasB
|
||||
output.entity = toPascalCase(
|
||||
["Link", serviceAObj.module, aliasA, serviceBObj.module, aliasB].join("_")
|
||||
)
|
||||
|
||||
const linkDefinition: ModuleJoinerConfig = {
|
||||
serviceName: output.serviceName,
|
||||
@@ -284,15 +356,7 @@ ${serviceBObj.module}: {
|
||||
{
|
||||
name: [output.entryPoint],
|
||||
args: {
|
||||
entity: toPascalCase(
|
||||
[
|
||||
"Link",
|
||||
serviceAObj.module,
|
||||
aliasA,
|
||||
serviceBObj.module,
|
||||
aliasB,
|
||||
].join("_")
|
||||
),
|
||||
entity: output.entity,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -324,15 +388,15 @@ ${serviceBObj.module}: {
|
||||
extends: [
|
||||
{
|
||||
serviceName: serviceAObj.module,
|
||||
fieldAlias: {
|
||||
[serviceBObj.isList ? pluralize(aliasB) : aliasB]: {
|
||||
path: aliasB + "_link." + aliasB,
|
||||
isList: serviceBObj.isList,
|
||||
forwardArgumentsOnPath: [aliasB + "_link." + aliasB],
|
||||
},
|
||||
},
|
||||
fieldAlias: buildFieldAlias({
|
||||
property: serviceBObj.isList ? pluralize(aliasB) : aliasB,
|
||||
path: aliasB + "_link." + aliasB,
|
||||
isList: serviceBObj.isList,
|
||||
forwardArguments: [aliasB + "_link." + aliasB],
|
||||
}),
|
||||
relationship: {
|
||||
serviceName: output.serviceName,
|
||||
entity: output.entity,
|
||||
primaryKey: serviceAObj.key,
|
||||
foreignKey: serviceAPrimaryKey,
|
||||
alias: aliasB + "_link",
|
||||
@@ -341,15 +405,15 @@ ${serviceBObj.module}: {
|
||||
},
|
||||
{
|
||||
serviceName: serviceBObj.module,
|
||||
fieldAlias: {
|
||||
[serviceAObj.isList ? pluralize(aliasA) : aliasA]: {
|
||||
path: aliasA + "_link." + aliasA,
|
||||
isList: serviceAObj.isList,
|
||||
forwardArgumentsOnPath: [aliasA + "_link." + aliasA],
|
||||
},
|
||||
},
|
||||
fieldAlias: buildFieldAlias({
|
||||
property: serviceAObj.isList ? pluralize(aliasA) : aliasA,
|
||||
path: aliasA + "_link." + aliasA,
|
||||
isList: serviceAObj.isList,
|
||||
forwardArguments: [aliasA + "_link." + aliasA],
|
||||
}),
|
||||
relationship: {
|
||||
serviceName: output.serviceName,
|
||||
entity: output.entity,
|
||||
primaryKey: serviceBObj.key,
|
||||
foreignKey: serviceBPrimaryKey,
|
||||
alias: aliasA + "_link",
|
||||
@@ -375,3 +439,62 @@ ${serviceBObj.module}: {
|
||||
|
||||
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