feat: refactor module joiner config and links generation (#7859)
* feat: refactor module joiner config and links generation * improve typings * WIP * WIP * WIP * rename type file * create link config * finish typings and add utils * improve links * WIP typings * finalize ExportModule utils * finalize ExportModule utils * fix: dml tests * improve and fixes * simplify typings with id changes * add toJSON * multiple fixes and entity builder fixes * fix currency searchable * fix tests * medusa service refactoring * cleanup * cleanup and fixes * make module name optional * renaming --------- Co-authored-by: Harminder Virk <virk.officials@gmail.com>
This commit is contained in:
committed by
GitHub
parent
5aa62e59e4
commit
617a5972bf
@@ -0,0 +1,53 @@
|
||||
import { model } from "../../../dml"
|
||||
|
||||
export const FulfillmentSet = {
|
||||
name: "FulfillmentSet",
|
||||
}
|
||||
export const ShippingOption = {
|
||||
name: "ShippingOption",
|
||||
}
|
||||
export const ShippingProfile = {
|
||||
name: "ShippingProfile",
|
||||
}
|
||||
export const Fulfillment = {
|
||||
name: "Fulfillment",
|
||||
}
|
||||
export const FulfillmentProvider = {
|
||||
name: "FulfillmentProvider",
|
||||
}
|
||||
export const ServiceZone = {
|
||||
name: "ServiceZone",
|
||||
}
|
||||
export const GeoZone = {
|
||||
name: "GeoZone",
|
||||
}
|
||||
export const ShippingOptionRule = {
|
||||
name: "ShippingOptionRule",
|
||||
}
|
||||
|
||||
export const dmlFulfillmentSet = model.define(FulfillmentSet.name, {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
export const dmlShippingOption = model.define(ShippingOption.name, {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
export const dmlShippingProfile = model.define(ShippingProfile.name, {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
export const dmlFulfillment = model.define(Fulfillment.name, {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
export const dmlFulfillmentProvider = model.define(FulfillmentProvider.name, {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
export const dmlServiceZone = model.define(ServiceZone.name, {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
|
||||
export const dmlGeoZone = model.define(GeoZone.name, {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
|
||||
export const dmlShippingOptionRule = model.define(ShippingOptionRule.name, {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
@@ -1,315 +1,505 @@
|
||||
import { defineJoinerConfig } from "../joiner-config-builder"
|
||||
import {
|
||||
buildLinkableKeysFromDmlObjects,
|
||||
buildLinkableKeysFromMikroOrmObjects,
|
||||
buildLinkConfigFromDmlObjects,
|
||||
defineJoinerConfig,
|
||||
} from "../joiner-config-builder"
|
||||
import { Modules } from "../definition"
|
||||
import { model } from "../../dml"
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import {
|
||||
dmlFulfillment,
|
||||
dmlFulfillmentProvider,
|
||||
dmlFulfillmentSet,
|
||||
dmlGeoZone,
|
||||
dmlServiceZone,
|
||||
dmlShippingOption,
|
||||
dmlShippingOptionRule,
|
||||
dmlShippingProfile,
|
||||
Fulfillment,
|
||||
FulfillmentProvider,
|
||||
FulfillmentSet,
|
||||
GeoZone,
|
||||
ServiceZone,
|
||||
ShippingOption,
|
||||
ShippingOptionRule,
|
||||
ShippingProfile,
|
||||
} from "../__fixtures__/joiner-config/entities"
|
||||
|
||||
const FulfillmentSet = {
|
||||
name: "FulfillmentSet",
|
||||
}
|
||||
const ShippingOption = {
|
||||
name: "ShippingOption",
|
||||
}
|
||||
const ShippingProfile = {
|
||||
name: "ShippingProfile",
|
||||
}
|
||||
const Fulfillment = {
|
||||
name: "Fulfillment",
|
||||
}
|
||||
const FulfillmentProvider = {
|
||||
name: "FulfillmentProvider",
|
||||
}
|
||||
const ServiceZone = {
|
||||
name: "ServiceZone",
|
||||
}
|
||||
const GeoZone = {
|
||||
name: "GeoZone",
|
||||
}
|
||||
const ShippingOptionRule = {
|
||||
name: "ShippingOptionRule",
|
||||
}
|
||||
describe("joiner-config-builder", () => {
|
||||
describe("defineJoiner | Mikro orm objects", () => {
|
||||
it("should return a full joiner configuration", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
models: [
|
||||
FulfillmentSet,
|
||||
ShippingOption,
|
||||
ShippingProfile,
|
||||
Fulfillment,
|
||||
FulfillmentProvider,
|
||||
ServiceZone,
|
||||
GeoZone,
|
||||
ShippingOptionRule,
|
||||
],
|
||||
})
|
||||
|
||||
describe("defineJoiner", () => {
|
||||
it("should return a full joiner configuration", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
entityQueryingConfig: [
|
||||
FulfillmentSet,
|
||||
ShippingOption,
|
||||
ShippingProfile,
|
||||
Fulfillment,
|
||||
FulfillmentProvider,
|
||||
ServiceZone,
|
||||
GeoZone,
|
||||
ShippingOptionRule,
|
||||
],
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {
|
||||
fulfillment_set_id: FulfillmentSet.name,
|
||||
shipping_option_id: ShippingOption.name,
|
||||
shipping_profile_id: ShippingProfile.name,
|
||||
fulfillment_id: Fulfillment.name,
|
||||
fulfillment_provider_id: FulfillmentProvider.name,
|
||||
service_zone_id: ServiceZone.name,
|
||||
geo_zone_id: GeoZone.name,
|
||||
shipping_option_rule_id: ShippingOptionRule.name,
|
||||
},
|
||||
alias: [
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: FulfillmentSet.name,
|
||||
methodSuffix: "FulfillmentSets",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option", "shipping_options"],
|
||||
args: {
|
||||
entity: ShippingOption.name,
|
||||
methodSuffix: "ShippingOptions",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_profile", "shipping_profiles"],
|
||||
args: {
|
||||
entity: ShippingProfile.name,
|
||||
methodSuffix: "ShippingProfiles",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment", "fulfillments"],
|
||||
args: {
|
||||
entity: Fulfillment.name,
|
||||
methodSuffix: "Fulfillments",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment_provider", "fulfillment_providers"],
|
||||
args: {
|
||||
entity: FulfillmentProvider.name,
|
||||
methodSuffix: "FulfillmentProviders",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["service_zone", "service_zones"],
|
||||
args: {
|
||||
entity: ServiceZone.name,
|
||||
methodSuffix: "ServiceZones",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["geo_zone", "geo_zones"],
|
||||
args: {
|
||||
entity: GeoZone.name,
|
||||
methodSuffix: "GeoZones",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option_rule", "shipping_option_rules"],
|
||||
args: {
|
||||
entity: ShippingOptionRule.name,
|
||||
methodSuffix: "ShippingOptionRules",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {
|
||||
fulfillment_set_id: FulfillmentSet.name,
|
||||
shipping_option_id: ShippingOption.name,
|
||||
shipping_profile_id: ShippingProfile.name,
|
||||
fulfillment_id: Fulfillment.name,
|
||||
fulfillment_provider_id: FulfillmentProvider.name,
|
||||
service_zone_id: ServiceZone.name,
|
||||
geo_zone_id: GeoZone.name,
|
||||
shipping_option_rule_id: ShippingOptionRule.name,
|
||||
},
|
||||
alias: [
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: FulfillmentSet.name,
|
||||
methodSuffix: "FulfillmentSets",
|
||||
it("should return a full joiner configuration with custom aliases", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option", "shipping_options"],
|
||||
args: {
|
||||
entity: ShippingOption.name,
|
||||
methodSuffix: "ShippingOptions",
|
||||
],
|
||||
})
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {},
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_profile", "shipping_profiles"],
|
||||
args: {
|
||||
entity: ShippingProfile.name,
|
||||
methodSuffix: "ShippingProfiles",
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a full joiner configuration with custom aliases and models", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
models: [
|
||||
FulfillmentSet,
|
||||
ShippingOption,
|
||||
ShippingProfile,
|
||||
Fulfillment,
|
||||
FulfillmentProvider,
|
||||
ServiceZone,
|
||||
GeoZone,
|
||||
ShippingOptionRule,
|
||||
],
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {
|
||||
fulfillment_set_id: FulfillmentSet.name,
|
||||
shipping_option_id: ShippingOption.name,
|
||||
shipping_profile_id: ShippingProfile.name,
|
||||
fulfillment_id: Fulfillment.name,
|
||||
fulfillment_provider_id: FulfillmentProvider.name,
|
||||
service_zone_id: ServiceZone.name,
|
||||
geo_zone_id: GeoZone.name,
|
||||
shipping_option_rule_id: ShippingOptionRule.name,
|
||||
},
|
||||
{
|
||||
name: ["fulfillment", "fulfillments"],
|
||||
args: {
|
||||
entity: Fulfillment.name,
|
||||
methodSuffix: "Fulfillments",
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment_provider", "fulfillment_providers"],
|
||||
args: {
|
||||
entity: FulfillmentProvider.name,
|
||||
methodSuffix: "FulfillmentProviders",
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: FulfillmentSet.name,
|
||||
methodSuffix: "FulfillmentSets",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["service_zone", "service_zones"],
|
||||
args: {
|
||||
entity: ServiceZone.name,
|
||||
methodSuffix: "ServiceZones",
|
||||
{
|
||||
name: ["shipping_option", "shipping_options"],
|
||||
args: {
|
||||
entity: ShippingOption.name,
|
||||
methodSuffix: "ShippingOptions",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["geo_zone", "geo_zones"],
|
||||
args: {
|
||||
entity: GeoZone.name,
|
||||
methodSuffix: "GeoZones",
|
||||
{
|
||||
name: ["shipping_profile", "shipping_profiles"],
|
||||
args: {
|
||||
entity: ShippingProfile.name,
|
||||
methodSuffix: "ShippingProfiles",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option_rule", "shipping_option_rules"],
|
||||
args: {
|
||||
entity: ShippingOptionRule.name,
|
||||
methodSuffix: "ShippingOptionRules",
|
||||
{
|
||||
name: ["fulfillment", "fulfillments"],
|
||||
args: {
|
||||
entity: Fulfillment.name,
|
||||
methodSuffix: "Fulfillments",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment_provider", "fulfillment_providers"],
|
||||
args: {
|
||||
entity: FulfillmentProvider.name,
|
||||
methodSuffix: "FulfillmentProviders",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["service_zone", "service_zones"],
|
||||
args: {
|
||||
entity: ServiceZone.name,
|
||||
methodSuffix: "ServiceZones",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["geo_zone", "geo_zones"],
|
||||
args: {
|
||||
entity: GeoZone.name,
|
||||
methodSuffix: "GeoZones",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option_rule", "shipping_option_rules"],
|
||||
args: {
|
||||
entity: ShippingOptionRule.name,
|
||||
methodSuffix: "ShippingOptionRules",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a full joiner configuration with custom aliases without method suffix", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {},
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a full joiner configuration with custom aliases overriding defaults", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
models: [FulfillmentSet],
|
||||
alias: [
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: "FulfillmentSet",
|
||||
methodSuffix: "fulfillmentSetCustom",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {
|
||||
fulfillment_set_id: FulfillmentSet.name,
|
||||
},
|
||||
],
|
||||
alias: [
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: "FulfillmentSet",
|
||||
methodSuffix: "fulfillmentSetCustom",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a full joiner configuration with custom aliases", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
describe("defineJoiner | DML objects", () => {
|
||||
it("should return a full joiner configuration", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
models: [
|
||||
dmlFulfillmentSet,
|
||||
dmlShippingOption,
|
||||
dmlShippingProfile,
|
||||
dmlFulfillment,
|
||||
dmlFulfillmentProvider,
|
||||
dmlServiceZone,
|
||||
dmlGeoZone,
|
||||
dmlShippingOptionRule,
|
||||
],
|
||||
})
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {},
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {
|
||||
fulfillment_set_id: FulfillmentSet.name,
|
||||
shipping_option_id: ShippingOption.name,
|
||||
shipping_profile_id: ShippingProfile.name,
|
||||
fulfillment_id: Fulfillment.name,
|
||||
fulfillment_provider_id: FulfillmentProvider.name,
|
||||
service_zone_id: ServiceZone.name,
|
||||
geo_zone_id: GeoZone.name,
|
||||
shipping_option_rule_id: ShippingOptionRule.name,
|
||||
},
|
||||
],
|
||||
alias: [
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: FulfillmentSet.name,
|
||||
methodSuffix: "FulfillmentSets",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option", "shipping_options"],
|
||||
args: {
|
||||
entity: ShippingOption.name,
|
||||
methodSuffix: "ShippingOptions",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_profile", "shipping_profiles"],
|
||||
args: {
|
||||
entity: ShippingProfile.name,
|
||||
methodSuffix: "ShippingProfiles",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment", "fulfillments"],
|
||||
args: {
|
||||
entity: Fulfillment.name,
|
||||
methodSuffix: "Fulfillments",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment_provider", "fulfillment_providers"],
|
||||
args: {
|
||||
entity: FulfillmentProvider.name,
|
||||
methodSuffix: "FulfillmentProviders",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["service_zone", "service_zones"],
|
||||
args: {
|
||||
entity: ServiceZone.name,
|
||||
methodSuffix: "ServiceZones",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["geo_zone", "geo_zones"],
|
||||
args: {
|
||||
entity: GeoZone.name,
|
||||
methodSuffix: "GeoZones",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option_rule", "shipping_option_rules"],
|
||||
args: {
|
||||
entity: ShippingOptionRule.name,
|
||||
methodSuffix: "ShippingOptionRules",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a full joiner configuration with custom aliases and models", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
entityQueryingConfig: [
|
||||
FulfillmentSet,
|
||||
ShippingOption,
|
||||
ShippingProfile,
|
||||
Fulfillment,
|
||||
FulfillmentProvider,
|
||||
ServiceZone,
|
||||
GeoZone,
|
||||
ShippingOptionRule,
|
||||
],
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
describe("buildLinkableKeysFromDmlObjects", () => {
|
||||
it("should return a linkableKeys object based on the DML's primary keys", () => {
|
||||
const user = model.define("user", {
|
||||
id: model.id().primaryKey(),
|
||||
name: model.text(),
|
||||
})
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {
|
||||
fulfillment_set_id: FulfillmentSet.name,
|
||||
shipping_option_id: ShippingOption.name,
|
||||
shipping_profile_id: ShippingProfile.name,
|
||||
fulfillment_id: Fulfillment.name,
|
||||
fulfillment_provider_id: FulfillmentProvider.name,
|
||||
service_zone_id: ServiceZone.name,
|
||||
geo_zone_id: GeoZone.name,
|
||||
shipping_option_rule_id: ShippingOptionRule.name,
|
||||
},
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: FulfillmentSet.name,
|
||||
methodSuffix: "FulfillmentSets",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option", "shipping_options"],
|
||||
args: {
|
||||
entity: ShippingOption.name,
|
||||
methodSuffix: "ShippingOptions",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_profile", "shipping_profiles"],
|
||||
args: {
|
||||
entity: ShippingProfile.name,
|
||||
methodSuffix: "ShippingProfiles",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment", "fulfillments"],
|
||||
args: {
|
||||
entity: Fulfillment.name,
|
||||
methodSuffix: "Fulfillments",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["fulfillment_provider", "fulfillment_providers"],
|
||||
args: {
|
||||
entity: FulfillmentProvider.name,
|
||||
methodSuffix: "FulfillmentProviders",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["service_zone", "service_zones"],
|
||||
args: {
|
||||
entity: ServiceZone.name,
|
||||
methodSuffix: "ServiceZones",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["geo_zone", "geo_zones"],
|
||||
args: {
|
||||
entity: GeoZone.name,
|
||||
methodSuffix: "GeoZones",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["shipping_option_rule", "shipping_option_rules"],
|
||||
args: {
|
||||
entity: ShippingOptionRule.name,
|
||||
methodSuffix: "ShippingOptionRules",
|
||||
},
|
||||
},
|
||||
],
|
||||
const car = model.define("car", {
|
||||
id: model.id(),
|
||||
number_plate: model.text().primaryKey(),
|
||||
test: model.text(),
|
||||
})
|
||||
|
||||
const linkableKeys = buildLinkableKeysFromDmlObjects([user, car])
|
||||
expectTypeOf(linkableKeys).toMatchTypeOf<{
|
||||
user_id: "User"
|
||||
car_number_plate: "Car"
|
||||
}>()
|
||||
|
||||
expect(linkableKeys).toEqual({
|
||||
user_id: user.name,
|
||||
car_number_plate: car.name,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a full joiner configuration with custom aliases without method suffix", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
describe("buildLinkableKeysFromMikroOrmObjects", () => {
|
||||
it("should return a linkableKeys object based on the mikro orm models name", () => {
|
||||
class User {}
|
||||
class Car {}
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {},
|
||||
alias: [
|
||||
{
|
||||
name: ["custom", "customs"],
|
||||
args: {
|
||||
entity: "Custom",
|
||||
methodSuffix: "Customs",
|
||||
},
|
||||
},
|
||||
],
|
||||
const linkableKeys = buildLinkableKeysFromMikroOrmObjects([Car, User])
|
||||
|
||||
expect(linkableKeys).toEqual({
|
||||
user_id: User.name,
|
||||
car_id: Car.name,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a full joiner configuration with custom aliases overriding defaults", () => {
|
||||
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
|
||||
entityQueryingConfig: [FulfillmentSet],
|
||||
alias: [
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: "FulfillmentSet",
|
||||
methodSuffix: "fulfillmentSetCustom",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
describe("buildLinkConfigFromDmlObjects", () => {
|
||||
it("should return a link config object based on the DML's primary keys", () => {
|
||||
const user = model.define("user", {
|
||||
id: model.id().primaryKey(),
|
||||
name: model.text(),
|
||||
})
|
||||
|
||||
expect(joinerConfig).toEqual({
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
primaryKeys: ["id"],
|
||||
schema: undefined,
|
||||
linkableKeys: {
|
||||
fulfillment_set_id: FulfillmentSet.name,
|
||||
},
|
||||
alias: [
|
||||
{
|
||||
name: ["fulfillment_set", "fulfillment_sets"],
|
||||
args: {
|
||||
entity: "FulfillmentSet",
|
||||
methodSuffix: "fulfillmentSetCustom",
|
||||
},
|
||||
},
|
||||
],
|
||||
const car = model.define("car", {
|
||||
id: model.id(),
|
||||
number_plate: model.text().primaryKey(),
|
||||
})
|
||||
|
||||
const linkConfig = buildLinkConfigFromDmlObjects([user, car])
|
||||
|
||||
expectTypeOf(linkConfig).toMatchTypeOf<{
|
||||
user: {
|
||||
id: {
|
||||
linkable: "user_id"
|
||||
primaryKey: "id"
|
||||
}
|
||||
toJSON: () => {
|
||||
linkable: string
|
||||
primaryKey: string
|
||||
}
|
||||
}
|
||||
car: {
|
||||
number_plate: {
|
||||
linkable: "car_number_plate"
|
||||
primaryKey: "number_plate"
|
||||
}
|
||||
toJSON: () => {
|
||||
linkable: string
|
||||
primaryKey: string
|
||||
}
|
||||
}
|
||||
}>()
|
||||
|
||||
expect(linkConfig.user.id).toEqual({
|
||||
linkable: "user_id",
|
||||
primaryKey: "id",
|
||||
})
|
||||
expect(linkConfig.car.number_plate).toEqual({
|
||||
linkable: "car_number_plate",
|
||||
primaryKey: "number_plate",
|
||||
})
|
||||
|
||||
expect(linkConfig.car.toJSON()).toEqual({
|
||||
linkable: "car_number_plate",
|
||||
primaryKey: "number_plate",
|
||||
})
|
||||
expect(linkConfig.user.toJSON()).toEqual({
|
||||
linkable: "user_id",
|
||||
primaryKey: "id",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Context, EventBusTypes } from "@medusajs/types"
|
||||
|
||||
// TODO should that move closer to the event bus? and maybe be rename to moduleEventBuilderFactory
|
||||
|
||||
/**
|
||||
*
|
||||
* Factory function to create event builders for different entities
|
||||
*
|
||||
* @example
|
||||
@@ -48,7 +51,8 @@ export function eventBuilderFactory({
|
||||
|
||||
// The event enums contains event formatted like so [object]_[action] e.g. PRODUCT_CREATED
|
||||
// We expect the keys of events to be fully uppercased
|
||||
const eventName = eventsEnum[`${object.toUpperCase()}_${action.toUpperCase()}`]
|
||||
const eventName =
|
||||
eventsEnum[`${object.toUpperCase()}_${action.toUpperCase()}`]
|
||||
|
||||
data.forEach((dataItem) => {
|
||||
messages.push({
|
||||
|
||||
@@ -13,4 +13,4 @@ export * from "./medusa-internal-service"
|
||||
export * from "./medusa-service"
|
||||
export * from "./migration-scripts"
|
||||
export * from "./mikro-orm-cli-config-builder"
|
||||
|
||||
export * from "./module"
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
import { JoinerServiceConfigAlias, ModuleJoinerConfig } from "@medusajs/types"
|
||||
import {
|
||||
JoinerServiceConfigAlias,
|
||||
ModuleJoinerConfig,
|
||||
PropertyType,
|
||||
} from "@medusajs/types"
|
||||
import { dirname, join } from "path"
|
||||
import {
|
||||
MapToConfig,
|
||||
camelToSnakeCase,
|
||||
deduplicate,
|
||||
getCallerFilePath,
|
||||
isObject,
|
||||
lowerCaseFirst,
|
||||
MapToConfig,
|
||||
pluralize,
|
||||
upperCaseFirst,
|
||||
} from "../common"
|
||||
import { loadModels } from "./loaders/load-models"
|
||||
import { DmlEntity } from "../dml"
|
||||
import { BaseRelationship } from "../dml/relations/base"
|
||||
import { PrimaryKeyModifier } from "../dml/properties/primary-key"
|
||||
import { InferLinkableKeys, InfersLinksConfig } from "./types/links-config"
|
||||
|
||||
/**
|
||||
* Define joiner config for a module based on the models (object representation or entities) present in the models directory. This action will be sync until
|
||||
@@ -20,7 +30,7 @@ import { loadModels } from "./loaders/load-models"
|
||||
* @param moduleName
|
||||
* @param alias
|
||||
* @param schema
|
||||
* @param entityQueryingConfig
|
||||
* @param models
|
||||
* @param linkableKeys
|
||||
* @param primaryKeys
|
||||
*/
|
||||
@@ -29,13 +39,13 @@ export function defineJoinerConfig(
|
||||
{
|
||||
alias,
|
||||
schema,
|
||||
entityQueryingConfig,
|
||||
models,
|
||||
linkableKeys,
|
||||
primaryKeys,
|
||||
}: {
|
||||
alias?: JoinerServiceConfigAlias[]
|
||||
schema?: string
|
||||
entityQueryingConfig?: { name: string }[]
|
||||
models?: DmlEntity<any, any>[] | { name: string }[]
|
||||
linkableKeys?: Record<string, string>
|
||||
primaryKeys?: string[]
|
||||
} = {}
|
||||
@@ -62,20 +72,64 @@ export function defineJoinerConfig(
|
||||
|
||||
basePath = join(basePath, "models")
|
||||
|
||||
const models = deduplicate(
|
||||
[...(entityQueryingConfig ?? loadModels(basePath))].flatMap((v) => v!.name)
|
||||
).map((name) => ({ name }))
|
||||
let loadedModels = models ?? loadModels(basePath)
|
||||
const modelDefinitions = new Map(
|
||||
loadedModels
|
||||
.filter((model) => !!DmlEntity.isDmlEntity(model))
|
||||
.map((model) => [model.name, model])
|
||||
)
|
||||
const mikroOrmObjects = new Map(
|
||||
loadedModels
|
||||
.filter((model) => !DmlEntity.isDmlEntity(model))
|
||||
.map((model) => [model.name, model])
|
||||
)
|
||||
|
||||
// We prioritize DML if there is any equivalent Mikro orm entities found
|
||||
loadedModels = [...modelDefinitions.values()]
|
||||
mikroOrmObjects.forEach((model) => {
|
||||
if (modelDefinitions.has(model.name)) {
|
||||
return
|
||||
}
|
||||
|
||||
loadedModels.push(model)
|
||||
})
|
||||
|
||||
if (!linkableKeys) {
|
||||
const linkableKeysFromDml = buildLinkableKeysFromDmlObjects([
|
||||
...modelDefinitions.values(),
|
||||
])
|
||||
const linkableKeysFromMikroOrm = buildLinkableKeysFromMikroOrmObjects([
|
||||
...mikroOrmObjects.values(),
|
||||
])
|
||||
linkableKeys = {
|
||||
...linkableKeysFromDml,
|
||||
...linkableKeysFromMikroOrm,
|
||||
}
|
||||
}
|
||||
|
||||
if (!primaryKeys && modelDefinitions.size) {
|
||||
const linkConfig = buildLinkConfigFromDmlObjects([
|
||||
...modelDefinitions.values(),
|
||||
])
|
||||
|
||||
primaryKeys = deduplicate(
|
||||
Object.values(linkConfig).flatMap((entityLinkConfig) => {
|
||||
return (Object.values(entityLinkConfig) as any[])
|
||||
.filter((linkableConfig) => isObject(linkableConfig))
|
||||
.map((linkableConfig) => {
|
||||
return linkableConfig.primaryKey
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: In the context of DML add a validation on primary keys and linkable keys if the consumer provide them manually. follow up pr
|
||||
|
||||
return {
|
||||
serviceName: moduleName,
|
||||
primaryKeys: primaryKeys ?? ["id"],
|
||||
schema,
|
||||
linkableKeys:
|
||||
linkableKeys ??
|
||||
models.reduce((acc, entity) => {
|
||||
acc[`${camelToSnakeCase(entity.name).toLowerCase()}_id`] = entity.name
|
||||
return acc
|
||||
}, {} as Record<string, string>),
|
||||
linkableKeys: linkableKeys,
|
||||
alias: [
|
||||
...[...(alias ?? ([] as any))].map((alias) => ({
|
||||
name: alias.name,
|
||||
@@ -86,7 +140,7 @@ export function defineJoinerConfig(
|
||||
pluralize(upperCaseFirst(alias.args.entity)),
|
||||
},
|
||||
})),
|
||||
...models
|
||||
...loadedModels
|
||||
.filter((model) => {
|
||||
return (
|
||||
!alias || !alias.some((alias) => alias.args?.entity === model.name)
|
||||
@@ -106,8 +160,153 @@ export function defineJoinerConfig(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From a set of DML objects, build the linkable keys
|
||||
*
|
||||
* @example
|
||||
* const user = model.define("user", {
|
||||
* id: model.id(),
|
||||
* name: model.text(),
|
||||
* })
|
||||
*
|
||||
* const car = model.define("car", {
|
||||
* id: model.id(),
|
||||
* number_plate: model.text().primaryKey(),
|
||||
* test: model.text(),
|
||||
* })
|
||||
*
|
||||
* // output:
|
||||
* // {
|
||||
* // user_id: 'User',
|
||||
* // car_number_plate: 'Car',
|
||||
* // }
|
||||
*
|
||||
* @param models
|
||||
*/
|
||||
export function buildLinkableKeysFromDmlObjects<
|
||||
const T extends DmlEntity<any, any>[],
|
||||
LinkableKeys = InferLinkableKeys<T>
|
||||
>(models: T): LinkableKeys {
|
||||
const linkableKeys = {} as LinkableKeys
|
||||
|
||||
for (const model of models) {
|
||||
if (!DmlEntity.isDmlEntity(model)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const schema = model.schema
|
||||
const primaryKeys: string[] = []
|
||||
|
||||
for (const [property, value] of Object.entries(schema)) {
|
||||
if (BaseRelationship.isRelationship(value)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const parsedProperty = (value as PropertyType<any>).parse(property)
|
||||
if (PrimaryKeyModifier.isPrimaryKeyModifier(value)) {
|
||||
const linkableKeyName =
|
||||
parsedProperty.dataType.options?.linkable ??
|
||||
`${camelToSnakeCase(model.name).toLowerCase()}_${property}`
|
||||
primaryKeys.push(linkableKeyName)
|
||||
}
|
||||
}
|
||||
|
||||
if (primaryKeys.length) {
|
||||
primaryKeys.forEach((primaryKey) => {
|
||||
linkableKeys[primaryKey] = model.name
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return linkableKeys
|
||||
}
|
||||
|
||||
/**
|
||||
* Build linkable keys from MikroORM objects
|
||||
* @deprecated
|
||||
* @param models
|
||||
*/
|
||||
export function buildLinkableKeysFromMikroOrmObjects(
|
||||
models: Function[]
|
||||
): Record<string, string> {
|
||||
return models.reduce((acc, entity) => {
|
||||
acc[`${camelToSnakeCase(entity.name).toLowerCase()}_id`] = entity.name
|
||||
return acc
|
||||
}, {}) as Record<string, string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Build entities name to linkable keys map
|
||||
*
|
||||
* @example
|
||||
* const user = model.define("user", {
|
||||
* id: model.id(),
|
||||
* name: model.text(),
|
||||
* })
|
||||
*
|
||||
* const car = model.define("car", {
|
||||
* id: model.id(),
|
||||
* number_plate: model.text().primaryKey(),
|
||||
* test: model.text(),
|
||||
* })
|
||||
*
|
||||
* // output:
|
||||
* // {
|
||||
* // toJSON: function () { },
|
||||
* // user: {
|
||||
* // id: "user_id",
|
||||
* // },
|
||||
* // car: {
|
||||
* // number_plate: "car_number_plate",
|
||||
* // },
|
||||
* // }
|
||||
*
|
||||
* @param models
|
||||
*/
|
||||
export function buildLinkConfigFromDmlObjects<
|
||||
const T extends DmlEntity<any, any>[]
|
||||
>(models: T = [] as unknown as T): InfersLinksConfig<T> {
|
||||
const linkConfig = {} as InfersLinksConfig<T>
|
||||
|
||||
for (const model of models) {
|
||||
if (!DmlEntity.isDmlEntity(model)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const schema = model.schema
|
||||
const modelLinkConfig = (linkConfig[lowerCaseFirst(model.name)] ??= {
|
||||
toJSON: function () {
|
||||
const linkables = Object.entries(this)
|
||||
.filter(([name]) => name !== "toJSON")
|
||||
.map(([, object]) => object)
|
||||
const lastIndex = linkables.length - 1
|
||||
return linkables[lastIndex]
|
||||
},
|
||||
})
|
||||
|
||||
for (const [property, value] of Object.entries(schema)) {
|
||||
if (BaseRelationship.isRelationship(value)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const parsedProperty = (value as PropertyType<any>).parse(property)
|
||||
if (PrimaryKeyModifier.isPrimaryKeyModifier(value)) {
|
||||
const linkableKeyName =
|
||||
parsedProperty.dataType.options?.linkable ??
|
||||
`${camelToSnakeCase(model.name).toLowerCase()}_${property}`
|
||||
modelLinkConfig[property] = {
|
||||
linkable: linkableKeyName,
|
||||
primaryKey: property,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return linkConfig as InfersLinksConfig<T> & Record<any, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* Reversed map from linkableKeys to entity name to linkable keys
|
||||
* @param linkableKeys
|
||||
*/
|
||||
export function buildEntitiesNameToLinkableKeysMap(
|
||||
|
||||
@@ -27,15 +27,15 @@ export function loadModels(basePath: string) {
|
||||
|
||||
if (stats.isFile()) {
|
||||
try {
|
||||
const required = require(filePath) as {
|
||||
[key: string]: { name?: string }
|
||||
}
|
||||
const required = require(filePath)
|
||||
|
||||
return Object.values(required).filter((resource) => !!resource.name)
|
||||
return Object.values(required).filter(
|
||||
(resource: any) => !!resource.name
|
||||
)
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
.filter(Boolean) as { name: string }[]
|
||||
.filter(Boolean) as any[]
|
||||
}
|
||||
|
||||
@@ -35,12 +35,16 @@ type SelectorAndData = {
|
||||
data: any
|
||||
}
|
||||
|
||||
export function MedusaInternalService<TContainer extends object = object>(
|
||||
export function MedusaInternalService<
|
||||
TContainer extends object = object,
|
||||
TEntity extends object = any
|
||||
>(
|
||||
rawModel: any
|
||||
): {
|
||||
new <TEntity extends object = any>(
|
||||
container: TContainer
|
||||
): ModulesSdkTypes.IMedusaInternalService<TEntity, TContainer>
|
||||
new (container: TContainer): ModulesSdkTypes.IMedusaInternalService<
|
||||
TEntity,
|
||||
TContainer
|
||||
>
|
||||
} {
|
||||
const model = DmlEntity.isDmlEntity(rawModel)
|
||||
? toMikroORMEntity(rawModel)
|
||||
@@ -49,7 +53,7 @@ export function MedusaInternalService<TContainer extends object = object>(
|
||||
const injectedRepositoryName = `${lowerCaseFirst(model.name)}Repository`
|
||||
const propertyRepositoryName = `__${injectedRepositoryName}__`
|
||||
|
||||
class AbstractService_<TEntity extends object>
|
||||
class AbstractService_
|
||||
implements ModulesSdkTypes.IMedusaInternalService<TEntity, TContainer>
|
||||
{
|
||||
readonly __container__: TContainer;
|
||||
@@ -556,7 +560,5 @@ export function MedusaInternalService<TContainer extends object = object>(
|
||||
}
|
||||
}
|
||||
|
||||
return AbstractService_ as unknown as new <TEntity extends {}>(
|
||||
container: TContainer
|
||||
) => ModulesSdkTypes.IMedusaInternalService<TEntity, TContainer>
|
||||
return AbstractService_ as any
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
* Utility factory and interfaces for module service public facing API
|
||||
*/
|
||||
import {
|
||||
Constructor,
|
||||
Context,
|
||||
FindConfig,
|
||||
IEventBusModuleService,
|
||||
Pluralize,
|
||||
ModuleJoinerConfig,
|
||||
RepositoryService,
|
||||
RestoreReturn,
|
||||
SoftDeleteReturn,
|
||||
@@ -22,17 +21,15 @@ import {
|
||||
} from "../common"
|
||||
import { InjectManager, MedusaContext } from "./decorators"
|
||||
import { ModuleRegistrationName } from "./definition"
|
||||
import { DmlEntity } from "../dml"
|
||||
|
||||
type BaseMethods =
|
||||
| "retrieve"
|
||||
| "list"
|
||||
| "listAndCount"
|
||||
| "delete"
|
||||
| "softDelete"
|
||||
| "restore"
|
||||
| "create"
|
||||
| "update"
|
||||
import {
|
||||
BaseMethods,
|
||||
EntitiesConfigTemplate,
|
||||
ExtractKeysFromConfig,
|
||||
MedusaServiceReturnType,
|
||||
ModelConfigurationsToConfigTemplate,
|
||||
TEntityEntries,
|
||||
} from "./types/medusa-service"
|
||||
import { buildEntitiesNameToLinkableKeysMap } from "./joiner-config-builder"
|
||||
|
||||
const readMethods = ["retrieve", "list", "listAndCount"] as BaseMethods[]
|
||||
const writeMethods = [
|
||||
@@ -45,200 +42,6 @@ const writeMethods = [
|
||||
|
||||
const methods: BaseMethods[] = [...readMethods, ...writeMethods]
|
||||
|
||||
type ModelDTOConfig = {
|
||||
dto: object
|
||||
create?: any
|
||||
update?: any
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
singular?: string
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
plural?: string
|
||||
}
|
||||
|
||||
type EntitiesConfigTemplate = { [key: string]: ModelDTOConfig }
|
||||
|
||||
type ModelConfigurationsToConfigTemplate<T extends TEntityEntries> = {
|
||||
[Key in keyof T as `${Capitalize<Key & string>}`]: {
|
||||
dto: T[Key] extends Constructor<any> ? InstanceType<T[Key]> : any
|
||||
create: any
|
||||
update: any
|
||||
singular: T[Key] extends { singular: string } ? T[Key]["singular"] : Key
|
||||
plural: T[Key] extends { plural: string }
|
||||
? T[Key]["plural"]
|
||||
: Pluralize<Key & string>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated should all notion of singular and plural be removed once all modules are aligned with the convention
|
||||
*/
|
||||
type ExtractSingularName<T extends Record<any, any>, K = keyof T> = Capitalize<
|
||||
T[K] extends { singular?: string } ? T[K]["singular"] & string : K & string
|
||||
>
|
||||
|
||||
/**
|
||||
* @deprecated should all notion of singular and plural be removed once all modules are aligned with the convention
|
||||
* The pluralize will move to where it should be used instead
|
||||
*/
|
||||
type ExtractPluralName<T extends Record<any, any>, K = keyof T> = Capitalize<
|
||||
T[K] extends {
|
||||
plural?: string
|
||||
}
|
||||
? T[K]["plural"] & string
|
||||
: Pluralize<K & string>
|
||||
>
|
||||
|
||||
// TODO: The future expected entry will be a DML object but in the meantime we have to maintain backward compatibility for ouw own modules and therefore we need to support Constructor<any> as well as this temporary object
|
||||
type TEntityEntries<Keys = string> = Record<
|
||||
Keys & string,
|
||||
| Constructor<any>
|
||||
| DmlEntity<any>
|
||||
| { name?: string; singular?: string; plural?: string }
|
||||
>
|
||||
|
||||
type ExtractKeysFromConfig<EntitiesConfig> = EntitiesConfig extends {
|
||||
__empty: any
|
||||
}
|
||||
? string
|
||||
: keyof EntitiesConfig
|
||||
|
||||
export type AbstractModuleService<
|
||||
TEntitiesDtoConfig extends Record<string, any>
|
||||
> = {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `retrieve${ExtractSingularName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: (
|
||||
id: string,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
) => Promise<TEntitiesDtoConfig[TEntityName]["dto"]>
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `list${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: (
|
||||
filters?: any,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
) => Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `listAndCount${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(filters?: any, config?: FindConfig<any>, sharedContext?: Context): Promise<
|
||||
[TEntitiesDtoConfig[TEntityName]["dto"][], number]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `delete${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `softDelete${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
<TReturnableLinkableKeys extends string>(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
config?: SoftDeleteReturn<TReturnableLinkableKeys>,
|
||||
sharedContext?: Context
|
||||
): Promise<Record<string, string[]> | void>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `restore${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
<TReturnableLinkableKeys extends string>(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
config?: RestoreReturn<TReturnableLinkableKeys>,
|
||||
sharedContext?: Context
|
||||
): Promise<Record<string, string[]> | void>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(...args: any[]): Promise<any>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(...args: any[]): Promise<any>
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Because of a bug, those methods were not made visible which now cause issues with the fix as our interface are not consistent with the expectations
|
||||
|
||||
// are not consistent accross modules
|
||||
/* & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(data: any[], sharedContext?: Context): Promise<
|
||||
TEntitiesDtoConfig[TEntityName]["dto"][]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(data: any, sharedContext?: Context): Promise<
|
||||
TEntitiesDtoConfig[TEntityName]["dto"][]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"][],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"]>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
idOrdSelector: any,
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@@ -263,6 +66,36 @@ function buildMethodNamesFromModel(
|
||||
}, {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessible from the MedusaService, holds the model objects when provided
|
||||
*/
|
||||
export const MedusaServiceModelObjectsSymbol = Symbol.for(
|
||||
"MedusaServiceModelObjectsSymbol"
|
||||
)
|
||||
|
||||
/**
|
||||
* Symbol to mark a class as a Medusa service
|
||||
*/
|
||||
export const MedusaServiceSymbol = Symbol.for("MedusaServiceSymbol")
|
||||
|
||||
/**
|
||||
* Accessible from the MedusaService, holds the entity name to linkable keys map
|
||||
* to be used for softDelete and restore methods
|
||||
*/
|
||||
export const MedusaServiceEntityNameToLinkableKeysMapSymbol = Symbol.for(
|
||||
"MedusaServiceEntityNameToLinkableKeysMapSymbol"
|
||||
)
|
||||
|
||||
/**
|
||||
* Check if a value is a Medusa service
|
||||
* @param value
|
||||
*/
|
||||
export function isMedusaService(
|
||||
value: any
|
||||
): value is MedusaServiceReturnType<any> {
|
||||
return value && value?.prototype[MedusaServiceSymbol]
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory function for creating an abstract module service
|
||||
*
|
||||
@@ -281,26 +114,22 @@ function buildMethodNamesFromModel(
|
||||
* RuleType,
|
||||
* }
|
||||
*
|
||||
* class MyService extends ModulesSdkUtils.MedusaService(entities, entityNameToLinkableKeysMap) {}
|
||||
* class MyService extends ModulesSdkUtils.MedusaService(entities) {}
|
||||
*
|
||||
* @param entities
|
||||
* @param entityNameToLinkableKeysMap
|
||||
*/
|
||||
export function MedusaService<
|
||||
EntitiesConfig extends EntitiesConfigTemplate = { __empty: any },
|
||||
TEntities extends TEntityEntries<
|
||||
const EntitiesConfig extends EntitiesConfigTemplate = { __empty: any },
|
||||
const TEntities extends TEntityEntries<
|
||||
ExtractKeysFromConfig<EntitiesConfig>
|
||||
> = TEntityEntries<ExtractKeysFromConfig<EntitiesConfig>>
|
||||
>(
|
||||
entities: TEntities,
|
||||
entityNameToLinkableKeysMap: MapToConfig = {}
|
||||
): {
|
||||
new (...args: any[]): AbstractModuleService<
|
||||
EntitiesConfig extends { __empty: any }
|
||||
? ModelConfigurationsToConfigTemplate<TEntities>
|
||||
: EntitiesConfig
|
||||
>
|
||||
} {
|
||||
entities: TEntities
|
||||
): MedusaServiceReturnType<
|
||||
EntitiesConfig extends { __empty: any }
|
||||
? ModelConfigurationsToConfigTemplate<TEntities>
|
||||
: EntitiesConfig
|
||||
> {
|
||||
const buildAndAssignMethodImpl = function (
|
||||
klassPrototype: any,
|
||||
method: string,
|
||||
@@ -474,7 +303,7 @@ export function MedusaService<
|
||||
// eg: product.id = product_id, variant.id = variant_id
|
||||
const mappedCascadedEntitiesMap = mapObjectTo(
|
||||
cascadedEntitiesMap,
|
||||
entityNameToLinkableKeysMap,
|
||||
this[MedusaServiceEntityNameToLinkableKeysMapSymbol],
|
||||
{
|
||||
pick: config.returnLinkableKeys,
|
||||
}
|
||||
@@ -506,7 +335,7 @@ export function MedusaService<
|
||||
// eg: product.id = product_id, variant.id = variant_id
|
||||
mappedCascadedEntitiesMap = mapObjectTo(
|
||||
cascadedEntitiesMap,
|
||||
entityNameToLinkableKeysMap,
|
||||
this[MedusaServiceEntityNameToLinkableKeysMapSymbol],
|
||||
{
|
||||
pick: config.returnLinkableKeys,
|
||||
}
|
||||
@@ -522,11 +351,23 @@ export function MedusaService<
|
||||
}
|
||||
|
||||
class AbstractModuleService_ {
|
||||
[MedusaServiceSymbol] = true
|
||||
|
||||
static [MedusaServiceModelObjectsSymbol] = Object.values(
|
||||
entities
|
||||
) as unknown as MedusaServiceReturnType<
|
||||
EntitiesConfig extends { __empty: any }
|
||||
? ModelConfigurationsToConfigTemplate<TEntities>
|
||||
: EntitiesConfig
|
||||
>["$modelObjects"];
|
||||
|
||||
[MedusaServiceEntityNameToLinkableKeysMapSymbol]: MapToConfig
|
||||
|
||||
readonly __container__: Record<any, any>
|
||||
readonly baseRepository_: RepositoryService
|
||||
readonly eventBusModuleService_: IEventBusModuleService;
|
||||
readonly eventBusModuleService_: IEventBusModuleService
|
||||
|
||||
[key: string]: any
|
||||
__joinerConfig?(): ModuleJoinerConfig
|
||||
|
||||
constructor(container: Record<any, any>) {
|
||||
this.__container__ = container
|
||||
@@ -539,6 +380,11 @@ export function MedusaService<
|
||||
this.eventBusModuleService_ = hasEventBusModuleService
|
||||
? this.__container__.eventBusModuleService
|
||||
: undefined
|
||||
|
||||
this[MedusaServiceEntityNameToLinkableKeysMapSymbol] =
|
||||
buildEntitiesNameToLinkableKeysMap(
|
||||
this.__joinerConfig?.()?.linkableKeys ?? {}
|
||||
)
|
||||
}
|
||||
|
||||
protected async emitEvents_(groupedEvents) {
|
||||
@@ -563,7 +409,7 @@ export function MedusaService<
|
||||
string,
|
||||
TEntities[keyof TEntities],
|
||||
Record<string, string>
|
||||
][] = Object.entries(entities).map(([name, config]) => [
|
||||
][] = Object.entries(entities as {}).map(([name, config]) => [
|
||||
name,
|
||||
config as TEntities[keyof TEntities],
|
||||
buildMethodNamesFromModel(name, config as TEntities[keyof TEntities]),
|
||||
|
||||
@@ -14,7 +14,7 @@ type Options = Partial<MikroORMOptions> & {
|
||||
| EntityClass<AnyEntity>
|
||||
| EntityClassGroup<AnyEntity>
|
||||
| EntitySchema
|
||||
| DmlEntity<any>
|
||||
| DmlEntity<any, any>
|
||||
)[]
|
||||
databaseName: string
|
||||
}
|
||||
|
||||
45
packages/core/utils/src/modules-sdk/module.ts
Normal file
45
packages/core/utils/src/modules-sdk/module.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Constructor, ModuleExports } from "@medusajs/types"
|
||||
import { MedusaServiceModelObjectsSymbol } from "./medusa-service"
|
||||
import {
|
||||
buildLinkConfigFromDmlObjects,
|
||||
defineJoinerConfig,
|
||||
} from "./joiner-config-builder"
|
||||
import { InfersLinksConfig } from "./types/links-config"
|
||||
import { DmlEntity } from "../dml"
|
||||
|
||||
/**
|
||||
* Wrapper to build the module export and auto generate the joiner config if needed as well as
|
||||
* return a links object based on the DML objects
|
||||
* @param moduleName
|
||||
* @param service
|
||||
* @param loaders
|
||||
* @constructor
|
||||
*/
|
||||
export function Module<
|
||||
const Service extends Constructor<any>,
|
||||
const ModelObjects extends DmlEntity<any, any>[] = Service extends {
|
||||
$modelObjects: infer $DmlObjects
|
||||
}
|
||||
? $DmlObjects
|
||||
: [],
|
||||
Links = keyof ModelObjects extends never
|
||||
? Record<string, any>
|
||||
: InfersLinksConfig<ModelObjects>
|
||||
>({
|
||||
name = "",
|
||||
service,
|
||||
loaders,
|
||||
}: ModuleExports<Service> & { name?: string }): ModuleExports<Service> & {
|
||||
links: Links
|
||||
} {
|
||||
service.prototype.__joinerConfig ??= defineJoinerConfig(name)
|
||||
|
||||
const dmlObjects = service[MedusaServiceModelObjectsSymbol]
|
||||
return {
|
||||
service,
|
||||
loaders,
|
||||
links: (dmlObjects?.length
|
||||
? buildLinkConfigFromDmlObjects(dmlObjects)
|
||||
: {}) as Links,
|
||||
}
|
||||
}
|
||||
131
packages/core/utils/src/modules-sdk/types/links-config.ts
Normal file
131
packages/core/utils/src/modules-sdk/types/links-config.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import {
|
||||
DMLSchema,
|
||||
IDmlEntityConfig,
|
||||
InferDmlEntityNameFromConfig,
|
||||
SnakeCase,
|
||||
} from "@medusajs/types"
|
||||
import { DmlEntity } from "../../dml"
|
||||
import { PrimaryKeyModifier } from "../../dml/properties/primary-key"
|
||||
|
||||
type FlattenUnion<T> = T extends { [K in keyof T]: infer U }
|
||||
? { [K in keyof T]: U }
|
||||
: never
|
||||
|
||||
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
||||
k: infer I
|
||||
) => void
|
||||
? I
|
||||
: never
|
||||
|
||||
type InferLinkableKeyName<
|
||||
Key,
|
||||
Property,
|
||||
DmlConfig extends IDmlEntityConfig
|
||||
> = Property extends PrimaryKeyModifier<any, any>
|
||||
? `${Lowercase<SnakeCase<InferDmlEntityNameFromConfig<DmlConfig>>>}_${Key &
|
||||
string}`
|
||||
: never
|
||||
|
||||
type InferSchemaLinkableKeys<T> = T extends DmlEntity<
|
||||
infer Schema,
|
||||
infer Config
|
||||
>
|
||||
? {
|
||||
[K in keyof Schema as Schema[K] extends PrimaryKeyModifier<any, any>
|
||||
? InferLinkableKeyName<K, Schema[K], Config>
|
||||
: never]: InferDmlEntityNameFromConfig<Config>
|
||||
}
|
||||
: {}
|
||||
|
||||
type InferSchemasLinkableKeys<T extends DmlEntity<any, any>[]> = {
|
||||
[K in keyof T]: InferSchemaLinkableKeys<T[K]>
|
||||
}
|
||||
|
||||
type AggregateSchemasLinkableKeys<T extends DmlEntity<any, any>[]> = {
|
||||
[K in keyof InferSchemasLinkableKeys<T>]: InferSchemasLinkableKeys<T>[K]
|
||||
}
|
||||
|
||||
/**
|
||||
* From an array of DmlEntity, returns a formatted object with the linkable keys
|
||||
*
|
||||
* @example:
|
||||
*
|
||||
* const user = model.define("user", {
|
||||
* id: model.id(),
|
||||
* name: model.text(),
|
||||
* })
|
||||
*
|
||||
* const car = model.define("car", {
|
||||
* id: model.id(),
|
||||
* number_plate: model.text().primaryKey(),
|
||||
* test: model.text(),
|
||||
* })
|
||||
*
|
||||
* const linkableKeys = buildLinkableKeysFromDmlObjects([user, car]) // { user_id: 'user', car_number_plate: 'car' }
|
||||
*
|
||||
*/
|
||||
export type InferLinkableKeys<T extends DmlEntity<any, any>[]> =
|
||||
UnionToIntersection<FlattenUnion<AggregateSchemasLinkableKeys<T>>[0]>
|
||||
|
||||
type InferPrimaryKeyNameOrNever<
|
||||
Schema extends DMLSchema,
|
||||
Key extends keyof Schema
|
||||
> = Schema[Key] extends PrimaryKeyModifier<any, any> ? Key : never
|
||||
|
||||
type InferSchemaLinksConfig<T> = T extends DmlEntity<infer Schema, infer Config>
|
||||
? {
|
||||
[K in keyof Schema as Schema[K] extends PrimaryKeyModifier<any, any>
|
||||
? InferPrimaryKeyNameOrNever<Schema, K>
|
||||
: never]: {
|
||||
linkable: InferLinkableKeyName<K, Schema[K], Config>
|
||||
primaryKey: K
|
||||
}
|
||||
}
|
||||
: {}
|
||||
|
||||
/**
|
||||
* From an array of DmlEntity, returns a formatted object with the linkable keys
|
||||
*
|
||||
* @example:
|
||||
*
|
||||
* const user = model.define("user", {
|
||||
* id: model.id(),
|
||||
* name: model.text(),
|
||||
* })
|
||||
*
|
||||
* const car = model.define("car", {
|
||||
* id: model.id(),
|
||||
* number_plate: model.text().primaryKey(),
|
||||
* test: model.text(),
|
||||
* })
|
||||
*
|
||||
* const linkConfig = buildLinkConfigFromDmlObjects([user, car])
|
||||
* // {
|
||||
* // user: {
|
||||
* // id: {
|
||||
* // linkable: 'user_id',
|
||||
* // primaryKey: 'id'
|
||||
* // },
|
||||
* // toJSON() { ... }
|
||||
* // },
|
||||
* // car: {
|
||||
* // number_plate: {
|
||||
* // linkable: 'car_number_plate',
|
||||
* // primaryKey: 'number_plate'
|
||||
* // },
|
||||
* // toJSON() { ... }
|
||||
* // }
|
||||
* // }
|
||||
*
|
||||
*/
|
||||
export type InfersLinksConfig<T extends DmlEntity<any, any>[]> =
|
||||
UnionToIntersection<{
|
||||
[K in keyof T as T[K] extends DmlEntity<any, infer Config>
|
||||
? Uncapitalize<InferDmlEntityNameFromConfig<Config>>
|
||||
: never]: InferSchemaLinksConfig<T[K]> & {
|
||||
toJSON: () => {
|
||||
linkable: string
|
||||
primaryKey: string
|
||||
}
|
||||
}
|
||||
}>
|
||||
261
packages/core/utils/src/modules-sdk/types/medusa-service.ts
Normal file
261
packages/core/utils/src/modules-sdk/types/medusa-service.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
import {
|
||||
Constructor,
|
||||
Context,
|
||||
FindConfig,
|
||||
Pluralize,
|
||||
RestoreReturn,
|
||||
SoftDeleteReturn,
|
||||
} from "@medusajs/types"
|
||||
import { DmlEntity } from "../../dml"
|
||||
|
||||
export type BaseMethods =
|
||||
| "retrieve"
|
||||
| "list"
|
||||
| "listAndCount"
|
||||
| "delete"
|
||||
| "softDelete"
|
||||
| "restore"
|
||||
| "create"
|
||||
| "update"
|
||||
|
||||
export type ModelDTOConfig = {
|
||||
dto: object
|
||||
model?: DmlEntity<any, any>
|
||||
create?: any
|
||||
update?: any
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
singular?: string
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
plural?: string
|
||||
}
|
||||
|
||||
export type EntitiesConfigTemplate = { [key: string]: ModelDTOConfig }
|
||||
|
||||
export type ModelConfigurationsToConfigTemplate<T extends TEntityEntries> = {
|
||||
[Key in keyof T]: {
|
||||
dto: T[Key] extends Constructor<any> ? InstanceType<T[Key]> : any
|
||||
model: T[Key] extends { model: infer MODEL }
|
||||
? MODEL
|
||||
: T[Key] extends DmlEntity<any, any>
|
||||
? T[Key]
|
||||
: never
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
create: any
|
||||
update: any
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
singular: T[Key] extends { singular: string } ? T[Key]["singular"] : Key
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
plural: T[Key] extends { plural: string }
|
||||
? T[Key]["plural"]
|
||||
: Pluralize<Key & string>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated should all notion of singular and plural be removed once all modules are aligned with the convention
|
||||
*/
|
||||
export type ExtractSingularName<
|
||||
T extends Record<any, any>,
|
||||
K = keyof T
|
||||
> = Capitalize<
|
||||
T[K] extends { singular?: string } ? T[K]["singular"] & string : K & string
|
||||
>
|
||||
|
||||
/**
|
||||
* @deprecated should all notion of singular and plural be removed once all modules are aligned with the convention
|
||||
* The pluralize will move to where it should be used instead
|
||||
*/
|
||||
export type ExtractPluralName<
|
||||
T extends Record<any, any>,
|
||||
K = keyof T
|
||||
> = Capitalize<
|
||||
T[K] extends {
|
||||
plural?: string
|
||||
}
|
||||
? T[K]["plural"] & string
|
||||
: Pluralize<K & string>
|
||||
>
|
||||
|
||||
// TODO: The future expected entry will be a MODEL object but in the meantime we have to maintain backward compatibility for ouw own modules and therefore we need to support Constructor<any> as well as this temporary object
|
||||
export type TEntityEntries<Keys = string> = Record<
|
||||
Keys & string,
|
||||
| DmlEntity<any, any>
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
| Constructor<any>
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
| { name?: string; singular?: string; plural?: string }
|
||||
>
|
||||
|
||||
export type ExtractKeysFromConfig<EntitiesConfig> = EntitiesConfig extends {
|
||||
__empty: any
|
||||
}
|
||||
? string
|
||||
: keyof EntitiesConfig
|
||||
|
||||
export type AbstractModuleService<
|
||||
TEntitiesDtoConfig extends Record<string, any>
|
||||
> = {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `retrieve${ExtractSingularName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: (
|
||||
id: string,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
) => Promise<TEntitiesDtoConfig[TEntityName]["dto"]>
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `list${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: (
|
||||
filters?: any,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
) => Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `listAndCount${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(filters?: any, config?: FindConfig<any>, sharedContext?: Context): Promise<
|
||||
[TEntitiesDtoConfig[TEntityName]["dto"][], number]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `delete${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `softDelete${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
<TReturnableLinkableKeys extends string>(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
config?: SoftDeleteReturn<TReturnableLinkableKeys>,
|
||||
sharedContext?: Context
|
||||
): Promise<Record<string, string[]> | void>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `restore${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
<TReturnableLinkableKeys extends string>(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
config?: RestoreReturn<TReturnableLinkableKeys>,
|
||||
sharedContext?: Context
|
||||
): Promise<Record<string, string[]> | void>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(...args: any[]): Promise<any>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(...args: any[]): Promise<any>
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Because of a bug, those methods were not made visible which now cause issues with the fix as our interface are not consistent with the expectations
|
||||
|
||||
// are not consistent accross modules
|
||||
/* & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(data: any[], sharedContext?: Context): Promise<
|
||||
TEntitiesDtoConfig[TEntityName]["dto"][]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(data: any, sharedContext?: Context): Promise<
|
||||
TEntitiesDtoConfig[TEntityName]["dto"][]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"][],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"]>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
idOrdSelector: any,
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
}
|
||||
}*/
|
||||
|
||||
type InferModelFromConfig<T> = {
|
||||
[K in keyof T as T[K] extends { model: any }
|
||||
? K
|
||||
: K extends DmlEntity<any, any>
|
||||
? K
|
||||
: never]: T[K] extends {
|
||||
model: infer MODEL
|
||||
}
|
||||
? MODEL extends DmlEntity<any, any>
|
||||
? MODEL
|
||||
: never
|
||||
: T[K] extends DmlEntity<any, any>
|
||||
? T[K]
|
||||
: never
|
||||
}
|
||||
|
||||
export type MedusaServiceReturnType<ModelsConfig extends Record<any, any>> = {
|
||||
new (...args: any[]): AbstractModuleService<ModelsConfig>
|
||||
$modelObjects: InferModelFromConfig<ModelsConfig>[keyof InferModelFromConfig<ModelsConfig>][]
|
||||
}
|
||||
Reference in New Issue
Block a user