@medusajs/utils
Version:
Medusa utilities functions shared by Medusa core and Modules
340 lines (336 loc) • 13.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefineLinkSymbol = void 0;
exports.defineLink = defineLink;
const common_1 = require("../common");
const compose_link_name_1 = require("../link/compose-link-name");
exports.DefineLinkSymbol = Symbol.for("DefineLink");
function isInputOptions(input) {
return (0, common_1.isObject)(input) && input?.["linkable"];
}
function isInputSource(input) {
return ((0, common_1.isObject)(input) && input?.["serviceName"]) || input?.["toJSON"];
}
function isToJSON(input) {
return (0, common_1.isObject)(input) && input?.["toJSON"];
}
function buildFieldAlias(fieldAliases) {
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) {
let serviceConfig = {};
if (isInputSource(input)) {
const source = isToJSON(input) ? input.toJSON() : input;
serviceConfig = {
key: source.linkable,
alias: source.alias ?? (0, common_1.camelToSnakeCase)(source.field ?? ""),
field: input.field ?? source.field,
primaryKey: source.primaryKey,
isList: false,
hasMany: false,
deleteCascade: false,
filterable: source.filterable,
module: source.serviceName,
entity: source.entity,
};
}
else if (isInputOptions(input)) {
const source = isToJSON(input.linkable)
? input.linkable.toJSON()
: input.linkable;
const hasMany = !!input.isList;
serviceConfig = {
key: source.linkable,
alias: source.alias ?? (0, common_1.camelToSnakeCase)(source.field ?? ""),
field: input.field ?? source.field,
primaryKey: source.primaryKey,
isList: input.isList ?? false,
hasMany,
deleteCascade: input.deleteCascade ?? false,
filterable: input.filterable,
module: source.serviceName,
entity: source.entity,
};
}
else {
throw new Error(`Invalid linkable passed for the argument\n${JSON.stringify(input, null, 2)}`);
}
return serviceConfig;
}
/**
* Generate a ModuleJoinerConfig for the link definition on the fly.
* All naming, aliases etc are following our conventional naming.
*
* @param leftService
* @param rightService
* @param linkServiceOptions
*/
function defineLink(leftService, rightService, linkServiceOptions) {
const serviceAObj = prepareServiceConfig(leftService);
const serviceBObj = prepareServiceConfig(rightService);
if (linkServiceOptions?.readOnly) {
if (!leftService.linkable || !leftService.field) {
throw new Error(`ReadOnly link requires "linkable" and "field" to be defined for the left service.`);
}
else if (leftService.filterable ||
rightService.filterable) {
throw new Error(`ReadOnly link does not support filterable fields.`);
}
return defineReadOnlyLink(serviceAObj, serviceBObj, linkServiceOptions);
}
const output = {
[exports.DefineLinkSymbol]: true,
serviceName: "",
entity: "",
entryPoint: "",
};
const register = function (modules) {
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
}
}`);
}
const serviceAKeyEntity = serviceAInfo.linkableKeys?.[serviceAObj.key];
const serviceBKeyInfo = serviceBInfo.linkableKeys?.[serviceBObj.key];
if (!serviceAKeyEntity) {
throw new Error(`Key ${serviceAObj.key} is not linkable on service ${serviceAObj.module}`);
}
if (!serviceBKeyInfo) {
throw new Error(`Key ${serviceBObj.key} is not linkable on service ${serviceBObj.module}`);
}
let serviceAAliases = serviceAInfo.alias ?? [];
if (!Array.isArray(serviceAAliases)) {
serviceAAliases = [serviceAAliases];
}
let aliasAOptions = serviceAObj.alias ??
serviceAAliases.find((a) => {
return a.entity == serviceAKeyEntity;
})?.name;
let aliasA = aliasAOptions;
if (Array.isArray(aliasAOptions)) {
aliasA = aliasAOptions[0];
}
if (!aliasA) {
throw new Error(`You need to provide an alias for ${serviceAObj.module}.${serviceAObj.key}`);
}
const serviceAObjEntryPoint = (0, common_1.camelToSnakeCase)(serviceAObj.field);
const serviceAMethodSuffix = serviceAAliases.find((serviceAlias) => {
return Array.isArray(serviceAlias.name)
? serviceAlias.name.includes(serviceAObjEntryPoint)
: serviceAlias.name === serviceAObjEntryPoint;
})?.args?.methodSuffix;
let serviceBAliases = serviceBInfo.alias ?? [];
if (!Array.isArray(serviceBAliases)) {
serviceBAliases = [serviceBAliases];
}
let aliasBOptions = serviceBObj.alias ??
serviceBAliases.find((a) => {
return a.entity == serviceBKeyInfo;
})?.name;
let aliasB = aliasBOptions;
if (Array.isArray(aliasBOptions)) {
aliasB = aliasBOptions[0];
}
if (!aliasB) {
throw new Error(`You need to provide an alias for ${serviceBObj.module}.${serviceBObj.key}`);
}
const serviceBObjEntryPoint = (0, common_1.camelToSnakeCase)(serviceBObj.field);
const serviceBMethodSuffix = serviceBAliases.find((serviceAlias) => {
return Array.isArray(serviceAlias.name)
? serviceAlias.name.includes(serviceBObjEntryPoint)
: serviceAlias.name === serviceBObjEntryPoint;
})?.args?.methodSuffix;
const moduleAPrimaryKeys = serviceAInfo.primaryKeys ?? [];
let serviceAPrimaryKey = serviceAObj.primaryKey ??
linkServiceOptions?.pk?.[serviceAObj.module] ??
moduleAPrimaryKeys;
if (Array.isArray(serviceAPrimaryKey)) {
serviceAPrimaryKey = serviceAPrimaryKey[0];
}
const isModuleAPrimaryKeyValid = moduleAPrimaryKeys.includes(serviceAPrimaryKey);
if (!isModuleAPrimaryKeyValid) {
throw new Error(`Primary key ${serviceAPrimaryKey} is not defined on service ${serviceAObj.module}`);
}
const moduleBPrimaryKeys = serviceBInfo.primaryKeys ?? [];
let serviceBPrimaryKey = serviceBObj.primaryKey ??
linkServiceOptions?.pk?.[serviceBObj.module] ??
moduleBPrimaryKeys;
if (Array.isArray(serviceBPrimaryKey)) {
serviceBPrimaryKey = serviceBPrimaryKey[0];
}
const isModuleBPrimaryKeyValid = moduleBPrimaryKeys.includes(serviceBPrimaryKey);
if (!isModuleBPrimaryKeyValid) {
throw new Error(`Primary key ${serviceBPrimaryKey} is not defined on service ${serviceBObj.module}`);
}
output.serviceName = (0, compose_link_name_1.composeLinkName)(serviceAObj.module, aliasA, serviceBObj.module, aliasB);
output.entryPoint = aliasA + "_" + aliasB;
output.entity = (0, common_1.toPascalCase)(["Link", serviceAObj.module, aliasA, serviceBObj.module, aliasB].join("_"));
const linkDefinition = {
serviceName: output.serviceName,
isLink: true,
alias: [
{
name: [output.entryPoint],
args: {
entity: output.entity,
},
},
],
primaryKeys: ["id", serviceAObj.key, serviceBObj.key],
relationships: [
{
serviceName: serviceAObj.module,
entity: serviceAObj.entity,
primaryKey: serviceAPrimaryKey,
foreignKey: serviceAObj.key,
alias: aliasA,
args: {
methodSuffix: serviceAMethodSuffix,
},
deleteCascade: serviceAObj.deleteCascade,
filterable: serviceAObj.filterable,
hasMany: serviceAObj.hasMany,
},
{
serviceName: serviceBObj.module,
entity: serviceBObj.entity,
primaryKey: serviceBPrimaryKey,
foreignKey: serviceBObj.key,
alias: aliasB,
args: {
methodSuffix: serviceBMethodSuffix,
},
deleteCascade: serviceBObj.deleteCascade,
filterable: serviceBObj.filterable,
hasMany: serviceBObj.hasMany,
},
],
extends: [
{
serviceName: serviceAObj.module,
entity: serviceAObj.entity,
fieldAlias: buildFieldAlias({
property: serviceBObj.isList ? (0, common_1.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",
isList: serviceBObj.isList,
},
},
{
serviceName: serviceBObj.module,
entity: serviceBObj.entity,
fieldAlias: buildFieldAlias({
property: serviceAObj.isList ? (0, common_1.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",
isList: serviceAObj.isList,
},
},
],
};
if (linkServiceOptions?.database) {
const { table, idPrefix, extraColumns } = linkServiceOptions.database;
linkDefinition.databaseConfig = {
tableName: table,
idPrefix,
extraFields: extraColumns,
};
}
return linkDefinition;
};
global.MedusaModule.setCustomLink(register);
return output;
}
function defineReadOnlyLink(serviceAObj, serviceBObj, readOnlyLinkOptions) {
const register = function (modules) {
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,
entity: serviceAObj.entity,
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);
}
//# sourceMappingURL=define-link.js.map
;