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

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

View File

@@ -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)
}