1 parent
95c538c675
commit
5a8a889c6d
57 files changed
+1254
-391
No files matched your search
@@ -11,12 +11,12 @@ export const initialize = async (
|
|||||||
options?: InMemoryCacheModuleOptions | ExternalModuleDeclaration
|
options?: InMemoryCacheModuleOptions | ExternalModuleDeclaration
|
||||||
): Promise<ICacheService> => {
|
): Promise<ICacheService> => {
|
||||||
const serviceKey = Modules.CACHE
|
const serviceKey = Modules.CACHE
|
||||||
const loaded = await MedusaModule.bootstrap(
|
const loaded = await MedusaModule.bootstrap<ICacheService>(
|
||||||
serviceKey,
|
serviceKey,
|
||||||
"@medusajs/cache-inmemory",
|
"@medusajs/cache-inmemory",
|
||||||
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||||
undefined
|
undefined
|
||||||
)
|
)
|
||||||
|
|
||||||
return loaded[serviceKey] as ICacheService
|
return loaded[serviceKey]
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"include": ["src"],
|
"include": ["src", "integration-tests"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,12 @@ export const initialize = async (
|
|||||||
options?: RedisCacheModuleOptions | ExternalModuleDeclaration
|
options?: RedisCacheModuleOptions | ExternalModuleDeclaration
|
||||||
): Promise<ICacheService> => {
|
): Promise<ICacheService> => {
|
||||||
const serviceKey = Modules.CACHE
|
const serviceKey = Modules.CACHE
|
||||||
const loaded = await MedusaModule.bootstrap(
|
const loaded = await MedusaModule.bootstrap<ICacheService>(
|
||||||
serviceKey,
|
serviceKey,
|
||||||
"@medusajs/cache-redis",
|
"@medusajs/cache-redis",
|
||||||
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||||
undefined
|
undefined
|
||||||
)
|
)
|
||||||
|
|
||||||
return loaded[serviceKey] as ICacheService
|
return loaded[serviceKey]
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,10 @@ import { IEventBusService } from "@medusajs/types"
|
|||||||
|
|
||||||
export const initialize = async (): Promise<IEventBusService> => {
|
export const initialize = async (): Promise<IEventBusService> => {
|
||||||
const serviceKey = Modules.EVENT_BUS
|
const serviceKey = Modules.EVENT_BUS
|
||||||
const loaded = await MedusaModule.bootstrap(
|
const loaded = await MedusaModule.bootstrap<IEventBusService>(
|
||||||
serviceKey,
|
serviceKey,
|
||||||
"@medusajs/event-bus-local"
|
"@medusajs/event-bus-local"
|
||||||
)
|
)
|
||||||
|
|
||||||
return loaded[serviceKey] as IEventBusService
|
return loaded[serviceKey]
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,12 @@ export const initialize = async (
|
|||||||
options?: EventBusRedisModuleOptions | ExternalModuleDeclaration
|
options?: EventBusRedisModuleOptions | ExternalModuleDeclaration
|
||||||
): Promise<IEventBusService> => {
|
): Promise<IEventBusService> => {
|
||||||
const serviceKey = Modules.EVENT_BUS
|
const serviceKey = Modules.EVENT_BUS
|
||||||
const loaded = await MedusaModule.bootstrap(
|
const loaded = await MedusaModule.bootstrap<IEventBusService>(
|
||||||
serviceKey,
|
serviceKey,
|
||||||
"@medusajs/event-bus-redis",
|
"@medusajs/event-bus-redis",
|
||||||
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||||
undefined
|
undefined
|
||||||
)
|
)
|
||||||
|
|
||||||
return loaded[serviceKey] as IEventBusService
|
return loaded[serviceKey]
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ export const initialize = async (
|
|||||||
}
|
}
|
||||||
): Promise<IInventoryService> => {
|
): Promise<IInventoryService> => {
|
||||||
const serviceKey = Modules.INVENTORY
|
const serviceKey = Modules.INVENTORY
|
||||||
const loaded = await MedusaModule.bootstrap(
|
const loaded = await MedusaModule.bootstrap<IInventoryService>(
|
||||||
serviceKey,
|
serviceKey,
|
||||||
"@medusajs/inventory",
|
"@medusajs/inventory",
|
||||||
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||||
@@ -23,5 +23,5 @@ export const initialize = async (
|
|||||||
injectedDependencies
|
injectedDependencies
|
||||||
)
|
)
|
||||||
|
|
||||||
return loaded[serviceKey] as IInventoryService
|
return loaded[serviceKey]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { JoinerServiceConfig } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const joinerConfig: JoinerServiceConfig = {
|
||||||
|
serviceName: Modules.INVENTORY,
|
||||||
|
primaryKeys: ["id"],
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "inventory_items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "inventory",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "inventory_level",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "InventoryLevels",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "inventory_levels",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "InventoryLevels",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reservation_items",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "ReservationItems",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reservation_item",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "ReservationItems",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reservation",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "ReservationItems",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reservations",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "ReservationItems",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
IInventoryService,
|
IInventoryService,
|
||||||
InventoryItemDTO,
|
InventoryItemDTO,
|
||||||
InventoryLevelDTO,
|
InventoryLevelDTO,
|
||||||
|
JoinerServiceConfig,
|
||||||
MODULE_RESOURCE_TYPE,
|
MODULE_RESOURCE_TYPE,
|
||||||
ReservationItemDTO,
|
ReservationItemDTO,
|
||||||
SharedContext,
|
SharedContext,
|
||||||
@@ -23,6 +24,7 @@ import {
|
|||||||
MedusaError,
|
MedusaError,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import { joinerConfig } from "../joiner-config"
|
||||||
import InventoryItemService from "./inventory-item"
|
import InventoryItemService from "./inventory-item"
|
||||||
import InventoryLevelService from "./inventory-level"
|
import InventoryLevelService from "./inventory-level"
|
||||||
import ReservationItemService from "./reservation-item"
|
import ReservationItemService from "./reservation-item"
|
||||||
@@ -57,6 +59,10 @@ export default class InventoryService implements IInventoryService {
|
|||||||
this.reservationItemService_ = reservationItemService
|
this.reservationItemService_ = reservationItemService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__joinerConfig(): JoinerServiceConfig {
|
||||||
|
return joinerConfig
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists inventory items that match the given selector
|
* Lists inventory items that match the given selector
|
||||||
* @param selector - the selector to filter inventory items by
|
* @param selector - the selector to filter inventory items by
|
||||||
@@ -75,6 +81,13 @@ export default class InventoryService implements IInventoryService {
|
|||||||
context
|
context
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
async list(
|
||||||
|
selector: FilterableInventoryItemProps,
|
||||||
|
config: FindConfig<InventoryItemDTO> = { relations: [], skip: 0, take: 10 },
|
||||||
|
context: SharedContext = {}
|
||||||
|
): Promise<InventoryItemDTO[]> {
|
||||||
|
return await this.inventoryItemService_.list(selector, config, context)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists inventory levels that match the given selector
|
* Lists inventory levels that match the given selector
|
||||||
|
|||||||
@@ -42,7 +42,6 @@
|
|||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@medusajs/types": "^1.8.7",
|
|
||||||
"medusa-interfaces": "^1.3.7",
|
"medusa-interfaces": "^1.3.7",
|
||||||
"typeorm": "^0.3.16"
|
"typeorm": "^0.3.16"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe("models loader", () => {
|
|||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
try {
|
try {
|
||||||
models = modelsLoader({
|
models = await modelsLoader({
|
||||||
container,
|
container,
|
||||||
isTest: true,
|
isTest: true,
|
||||||
coreTestPathGlob: "../models/{product,product-variant}.ts",
|
coreTestPathGlob: "../models/{product,product-variant}.ts",
|
||||||
@@ -30,9 +30,9 @@ describe("models loader", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("ensure that the product model is an extended model", () => {
|
it("ensure that the product model is an extended model", () => {
|
||||||
const productModel = container.resolve("productModel")
|
const productModel = models.find((model) => model.name === "Product")
|
||||||
|
|
||||||
expect(productModel.custom_attribute).toEqual("test")
|
expect(new productModel().custom_attribute).toEqual("test")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("ensure that the extended product model is registered in db_entities", () => {
|
it("ensure that the extended product model is registered in db_entities", () => {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import path from "path"
|
|||||||
import { ClassConstructor, MedusaContainer } from "../types/global"
|
import { ClassConstructor, MedusaContainer } from "../types/global"
|
||||||
import { EntitySchema } from "typeorm"
|
import { EntitySchema } from "typeorm"
|
||||||
import { asClass, asValue } from "awilix"
|
import { asClass, asValue } from "awilix"
|
||||||
import { upperCaseFirst } from "@medusajs/utils"
|
|
||||||
|
|
||||||
type ModelLoaderParams = {
|
type ModelLoaderParams = {
|
||||||
container: MedusaContainer
|
container: MedusaContainer
|
||||||
@@ -63,9 +62,8 @@ export default (
|
|||||||
// If an extension file is found, override it with that instead
|
// If an extension file is found, override it with that instead
|
||||||
if (mappedExtensionModel) {
|
if (mappedExtensionModel) {
|
||||||
const coreModel = require(modelPath)
|
const coreModel = require(modelPath)
|
||||||
const modelName = upperCaseFirst(
|
const modelName =
|
||||||
formatRegistrationNameWithoutNamespace(modelPath)
|
formatRegistrationNameWithoutNamespace(modelPath)
|
||||||
)
|
|
||||||
|
|
||||||
coreModel[modelName] = mappedExtensionModel
|
coreModel[modelName] = mappedExtensionModel
|
||||||
val = mappedExtensionModel
|
val = mappedExtensionModel
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
import { SearchUtils, upperCaseFirst } from "@medusajs/utils"
|
|
||||||
import { aliasTo, asFunction, asValue, Lifetime } from "awilix"
|
|
||||||
import { Express } from "express"
|
|
||||||
import fs from "fs"
|
|
||||||
import { sync as existsSync } from "fs-exists-cached"
|
|
||||||
import glob from "glob"
|
|
||||||
import _ from "lodash"
|
|
||||||
import { createRequireFromPath } from "medusa-core-utils"
|
|
||||||
import {
|
|
||||||
FileService,
|
|
||||||
FulfillmentService,
|
|
||||||
OauthService,
|
|
||||||
} from "medusa-interfaces"
|
|
||||||
import { trackInstallation } from "medusa-telemetry"
|
|
||||||
import path from "path"
|
|
||||||
import { EntitySchema } from "typeorm"
|
|
||||||
import {
|
import {
|
||||||
AbstractTaxService,
|
AbstractTaxService,
|
||||||
isBatchJobStrategy,
|
isBatchJobStrategy,
|
||||||
@@ -23,23 +7,40 @@ import {
|
|||||||
isPriceSelectionStrategy,
|
isPriceSelectionStrategy,
|
||||||
isTaxCalculationStrategy,
|
isTaxCalculationStrategy,
|
||||||
} from "../interfaces"
|
} from "../interfaces"
|
||||||
import { MiddlewareService } from "../services"
|
|
||||||
import {
|
import {
|
||||||
ClassConstructor,
|
ClassConstructor,
|
||||||
ConfigModule,
|
ConfigModule,
|
||||||
Logger,
|
Logger,
|
||||||
MedusaContainer,
|
MedusaContainer,
|
||||||
} from "../types/global"
|
} from "../types/global"
|
||||||
|
import {
|
||||||
|
FileService,
|
||||||
|
FulfillmentService,
|
||||||
|
OauthService,
|
||||||
|
} from "medusa-interfaces"
|
||||||
|
import { Lifetime, aliasTo, asFunction, asValue } from "awilix"
|
||||||
|
import { SearchUtils, upperCaseFirst } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
formatRegistrationName,
|
formatRegistrationName,
|
||||||
formatRegistrationNameWithoutNamespace,
|
formatRegistrationNameWithoutNamespace,
|
||||||
} from "../utils/format-registration-name"
|
} from "../utils/format-registration-name"
|
||||||
import { getModelExtensionsMap } from "./helpers/get-model-extension-map"
|
|
||||||
import {
|
import {
|
||||||
registerPaymentProcessorFromClass,
|
registerPaymentProcessorFromClass,
|
||||||
registerPaymentServiceFromClass,
|
registerPaymentServiceFromClass,
|
||||||
} from "./helpers/plugins"
|
} from "./helpers/plugins"
|
||||||
|
|
||||||
|
import { EntitySchema } from "typeorm"
|
||||||
|
import { Express } from "express"
|
||||||
|
import { MiddlewareService } from "../services"
|
||||||
|
import _ from "lodash"
|
||||||
|
import { createRequireFromPath } from "medusa-core-utils"
|
||||||
|
import { sync as existsSync } from "fs-exists-cached"
|
||||||
|
import fs from "fs"
|
||||||
|
import { getModelExtensionsMap } from "./helpers/get-model-extension-map"
|
||||||
|
import glob from "glob"
|
||||||
import logger from "./logger"
|
import logger from "./logger"
|
||||||
|
import path from "path"
|
||||||
|
import { trackInstallation } from "medusa-telemetry"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
rootDirectory: string
|
rootDirectory: string
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
"typescript": "^4.4.4"
|
"typescript": "^4.4.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@medusajs/types": "^1.8.8",
|
"@medusajs/orchestration": "^0.0.2",
|
||||||
|
"@medusajs/types": "^1.8.11",
|
||||||
"@medusajs/utils": "^1.9.1",
|
"@medusajs/utils": "^1.9.1",
|
||||||
"awilix": "^8.0.0",
|
"awilix": "^8.0.0",
|
||||||
"resolve-cwd": "^3.0.0"
|
"resolve-cwd": "^3.0.0"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export enum Modules {
|
|||||||
STOCK_LOCATION = "stockLocationService",
|
STOCK_LOCATION = "stockLocationService",
|
||||||
INVENTORY = "inventoryService",
|
INVENTORY = "inventoryService",
|
||||||
CACHE = "cacheService",
|
CACHE = "cacheService",
|
||||||
PRODUCT = "productModuleService",
|
PRODUCT = "productService",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModulesDefinition: { [key: string]: ModuleDefinition } = {
|
export const ModulesDefinition: { [key: string]: ModuleDefinition } = {
|
||||||
@@ -33,6 +33,7 @@ export const ModulesDefinition: { [key: string]: ModuleDefinition } = {
|
|||||||
label: "StockLocationService",
|
label: "StockLocationService",
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
canOverride: true,
|
canOverride: true,
|
||||||
|
isQueryable: true,
|
||||||
dependencies: ["eventBusService"],
|
dependencies: ["eventBusService"],
|
||||||
defaultModuleDeclaration: {
|
defaultModuleDeclaration: {
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
@@ -46,6 +47,7 @@ export const ModulesDefinition: { [key: string]: ModuleDefinition } = {
|
|||||||
label: "InventoryService",
|
label: "InventoryService",
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
canOverride: true,
|
canOverride: true,
|
||||||
|
isQueryable: true,
|
||||||
dependencies: ["eventBusService"],
|
dependencies: ["eventBusService"],
|
||||||
defaultModuleDeclaration: {
|
defaultModuleDeclaration: {
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
@@ -66,11 +68,12 @@ export const ModulesDefinition: { [key: string]: ModuleDefinition } = {
|
|||||||
},
|
},
|
||||||
[Modules.PRODUCT]: {
|
[Modules.PRODUCT]: {
|
||||||
key: Modules.PRODUCT,
|
key: Modules.PRODUCT,
|
||||||
registrationName: Modules.PRODUCT,
|
registrationName: "productModuleService",
|
||||||
defaultPackage: false,
|
defaultPackage: false,
|
||||||
label: "ProductModuleService",
|
label: "ProductModuleService",
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
canOverride: true,
|
canOverride: true,
|
||||||
|
isQueryable: true,
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
defaultModuleDeclaration: {
|
defaultModuleDeclaration: {
|
||||||
scope: MODULE_SCOPE.EXTERNAL,
|
scope: MODULE_SCOPE.EXTERNAL,
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ export * from "./definitions"
|
|||||||
export * from "./loaders"
|
export * from "./loaders"
|
||||||
export * from "./medusa-module"
|
export * from "./medusa-module"
|
||||||
export * from "./module-helper"
|
export * from "./module-helper"
|
||||||
|
export * from "./remote-query"
|
||||||
@@ -3,27 +3,49 @@ import {
|
|||||||
MODULE_RESOURCE_TYPE,
|
MODULE_RESOURCE_TYPE,
|
||||||
MODULE_SCOPE,
|
MODULE_SCOPE,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { MedusaModule } from "../../medusa-module"
|
|
||||||
|
|
||||||
const mockRegisterMedusaModule = jest
|
import { MedusaModule } from "../../medusa-module"
|
||||||
.fn()
|
import { asValue } from "awilix"
|
||||||
.mockImplementation(() => Promise.resolve([]))
|
|
||||||
const mockModuleLoader = jest.fn().mockImplementation(() => Promise.resolve({}))
|
const mockRegisterMedusaModule = jest.fn().mockImplementation(() => {
|
||||||
|
return {
|
||||||
|
moduleKey: {
|
||||||
|
definition: {
|
||||||
|
key: "moduleKey",
|
||||||
|
registrationName: "moduleKey",
|
||||||
|
},
|
||||||
|
moduleDeclaration: {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const mockModuleLoader = jest.fn().mockImplementation(({ container }) => {
|
||||||
|
container.register({
|
||||||
|
moduleKey: asValue({}),
|
||||||
|
})
|
||||||
|
return Promise.resolve({})
|
||||||
|
})
|
||||||
|
|
||||||
jest.mock("./../../loaders", () => ({
|
jest.mock("./../../loaders", () => ({
|
||||||
registerMedusaModule: jest
|
registerMedusaModule: jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockImplementation((...args) => mockRegisterMedusaModule()),
|
.mockImplementation((...args) => mockRegisterMedusaModule()),
|
||||||
moduleLoader: jest.fn().mockImplementation((...args) => mockModuleLoader()),
|
moduleLoader: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((...args) => mockModuleLoader.apply(this, args)),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
describe("Medusa Module", () => {
|
describe("Medusa Modules", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
MedusaModule.clearInstances()
|
||||||
jest.resetModules()
|
jest.resetModules()
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("MedusaModule bootstrap - Singleton instances", async () => {
|
it("should create singleton instances", async () => {
|
||||||
await MedusaModule.bootstrap("moduleKey", "@path", {
|
await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
@@ -57,4 +79,182 @@ describe("Medusa Module", () => {
|
|||||||
expect(mockRegisterMedusaModule).toBeCalledTimes(2)
|
expect(mockRegisterMedusaModule).toBeCalledTimes(2)
|
||||||
expect(mockModuleLoader).toBeCalledTimes(2)
|
expect(mockModuleLoader).toBeCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should prevent the module being loaded multiple times under concurrent requests", async () => {
|
||||||
|
const load: any = []
|
||||||
|
|
||||||
|
for (let i = 5; i--; ) {
|
||||||
|
load.push(
|
||||||
|
MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
options: {
|
||||||
|
abc: 123,
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const intances = Promise.all(load)
|
||||||
|
|
||||||
|
expect(mockRegisterMedusaModule).toBeCalledTimes(1)
|
||||||
|
expect(mockModuleLoader).toBeCalledTimes(1)
|
||||||
|
expect(intances[(await intances).length - 1]).toBe(intances[0])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("getModuleInstance should return the first instance of the module if there is none flagged as 'main'", async () => {
|
||||||
|
const moduleA = await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
options: {
|
||||||
|
abc: 123,
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
const moduleB = await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
options: {
|
||||||
|
different_options: "abc",
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
expect(MedusaModule.getModuleInstance("moduleKey")).toEqual(moduleA)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return the module flagged as 'main' when multiple instances are available", async () => {
|
||||||
|
const moduleA = await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
options: {
|
||||||
|
abc: 123,
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
const moduleB = await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
main: true,
|
||||||
|
options: {
|
||||||
|
different_options: "abc",
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
expect(MedusaModule.getModuleInstance("moduleKey")).toEqual(moduleB)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should retrieve the module by their given alias", async () => {
|
||||||
|
const moduleA = await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
alias: "mod_A",
|
||||||
|
options: {
|
||||||
|
abc: 123,
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
const moduleB = await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
main: true,
|
||||||
|
alias: "mod_B",
|
||||||
|
options: {
|
||||||
|
different_options: "abc",
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
const moduleC = await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
alias: "mod_C",
|
||||||
|
options: {
|
||||||
|
moduleC: true,
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
// main
|
||||||
|
expect(MedusaModule.getModuleInstance("moduleKey")).toEqual(moduleB)
|
||||||
|
|
||||||
|
expect(MedusaModule.getModuleInstance("moduleKey", "mod_A")).toEqual(
|
||||||
|
moduleA
|
||||||
|
)
|
||||||
|
expect(MedusaModule.getModuleInstance("moduleKey", "mod_B")).toEqual(
|
||||||
|
moduleB
|
||||||
|
)
|
||||||
|
expect(MedusaModule.getModuleInstance("moduleKey", "mod_C")).toEqual(
|
||||||
|
moduleC
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should prevent two main modules being set as 'main'", async () => {
|
||||||
|
await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
alias: "mod_A",
|
||||||
|
options: {
|
||||||
|
abc: 123,
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
main: true,
|
||||||
|
alias: "mod_B",
|
||||||
|
options: {
|
||||||
|
different_options: "abc",
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
const moduleC = MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
main: true,
|
||||||
|
alias: "mod_C",
|
||||||
|
options: {
|
||||||
|
moduleC: true,
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
expect(moduleC).rejects.toThrow(
|
||||||
|
"Module moduleKey already have a 'main' registered."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should prevent the same alias be used for different instances of the same module", async () => {
|
||||||
|
await MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
alias: "module_alias",
|
||||||
|
options: {
|
||||||
|
different_options: "abc",
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
const moduleC = MedusaModule.bootstrap("moduleKey", "@path", {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
resolve: "@path",
|
||||||
|
alias: "module_alias",
|
||||||
|
options: {
|
||||||
|
moduleC: true,
|
||||||
|
},
|
||||||
|
} as InternalModuleDeclaration)
|
||||||
|
|
||||||
|
expect(moduleC).rejects.toThrow(
|
||||||
|
"Module moduleKey already registed as 'module_alias'. Please choose a different alias."
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
import {
|
import {
|
||||||
ExternalModuleDeclaration,
|
ExternalModuleDeclaration,
|
||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
|
JoinerServiceConfig,
|
||||||
MODULE_RESOURCE_TYPE,
|
MODULE_RESOURCE_TYPE,
|
||||||
MODULE_SCOPE,
|
MODULE_SCOPE,
|
||||||
|
ModuleDefinition,
|
||||||
ModuleExports,
|
ModuleExports,
|
||||||
|
ModuleResolution,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
createMedusaContainer,
|
createMedusaContainer,
|
||||||
simpleHash,
|
simpleHash,
|
||||||
stringifyCircular,
|
stringifyCircular,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { asValue } from "awilix"
|
|
||||||
import { moduleLoader, registerMedusaModule } from "./loaders"
|
import { moduleLoader, registerMedusaModule } from "./loaders"
|
||||||
|
|
||||||
|
import { asValue } from "awilix"
|
||||||
import { loadModuleMigrations } from "./loaders/utils"
|
import { loadModuleMigrations } from "./loaders/utils"
|
||||||
|
|
||||||
const logger: any = {
|
const logger: any = {
|
||||||
@@ -21,19 +25,105 @@ const logger: any = {
|
|||||||
error: (a) => console.error(a),
|
error: (a) => console.error(a),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface MedusaModule {
|
||||||
|
getLoadedModules(): Map<string, any>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModuleAlias = {
|
||||||
|
key: string
|
||||||
|
hash: string
|
||||||
|
alias?: string
|
||||||
|
main?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export class MedusaModule {
|
export class MedusaModule {
|
||||||
private static instances_: Map<string, any> = new Map()
|
private static instances_: Map<string, any> = new Map()
|
||||||
|
private static modules_: Map<string, ModuleAlias[]> = new Map()
|
||||||
|
private static loading_: Map<string, Promise<any>> = new Map()
|
||||||
|
|
||||||
|
public static getLoadedModules(): Map<
|
||||||
|
string,
|
||||||
|
any & {
|
||||||
|
__joinerConfig: JoinerServiceConfig
|
||||||
|
__definition: ModuleDefinition
|
||||||
|
}
|
||||||
|
> {
|
||||||
|
return MedusaModule.instances_
|
||||||
|
}
|
||||||
|
|
||||||
public static clearInstances(): void {
|
public static clearInstances(): void {
|
||||||
MedusaModule.instances_.clear()
|
MedusaModule.instances_.clear()
|
||||||
|
MedusaModule.modules_.clear()
|
||||||
}
|
}
|
||||||
public static async bootstrap(
|
|
||||||
|
public static isInstalled(moduleKey: string, alias?: string): boolean {
|
||||||
|
if (alias) {
|
||||||
|
return (
|
||||||
|
MedusaModule.modules_.has(moduleKey) &&
|
||||||
|
MedusaModule.modules_.get(moduleKey)!.some((m) => m.alias === alias)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return MedusaModule.modules_.has(moduleKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getModuleInstance(
|
||||||
|
moduleKey: string,
|
||||||
|
alias?: string
|
||||||
|
): any | undefined {
|
||||||
|
if (!MedusaModule.modules_.has(moduleKey)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let mod
|
||||||
|
const modules = MedusaModule.modules_.get(moduleKey)!
|
||||||
|
if (alias) {
|
||||||
|
mod = modules.find((m) => m.alias === alias)
|
||||||
|
|
||||||
|
return MedusaModule.instances_.get(mod?.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod = modules.find((m) => m.main) ?? modules[0]
|
||||||
|
|
||||||
|
return MedusaModule.instances_.get(mod?.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static registerModule(
|
||||||
|
moduleKey: string,
|
||||||
|
loadedModule: ModuleAlias
|
||||||
|
): void {
|
||||||
|
if (!MedusaModule.modules_.has(moduleKey)) {
|
||||||
|
MedusaModule.modules_.set(moduleKey, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
const modules = MedusaModule.modules_.get(moduleKey)!
|
||||||
|
|
||||||
|
if (modules.some((m) => m.alias === loadedModule.alias)) {
|
||||||
|
throw new Error(
|
||||||
|
`Module ${moduleKey} already registed as '${loadedModule.alias}'. Please choose a different alias.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedModule.main) {
|
||||||
|
if (modules.some((m) => m.main)) {
|
||||||
|
throw new Error(`Module ${moduleKey} already have a 'main' registered.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modules.push(loadedModule)
|
||||||
|
MedusaModule.modules_.set(moduleKey, modules!)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async bootstrap<T>(
|
||||||
moduleKey: string,
|
moduleKey: string,
|
||||||
defaultPath: string,
|
defaultPath: string,
|
||||||
declaration?: InternalModuleDeclaration | ExternalModuleDeclaration,
|
declaration?: InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||||
moduleExports?: ModuleExports,
|
moduleExports?: ModuleExports,
|
||||||
injectedDependencies?: Record<string, any>
|
injectedDependencies?: Record<string, any>
|
||||||
): Promise<{
|
): Promise<{
|
||||||
[key: string]: any
|
[key: string]: T
|
||||||
}> {
|
}> {
|
||||||
const hashKey = simpleHash(
|
const hashKey = simpleHash(
|
||||||
stringifyCircular({ moduleKey, defaultPath, declaration })
|
stringifyCircular({ moduleKey, defaultPath, declaration })
|
||||||
@@ -43,13 +133,32 @@ export class MedusaModule {
|
|||||||
return MedusaModule.instances_.get(hashKey)
|
return MedusaModule.instances_.get(hashKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
let modDeclaration = declaration
|
if (MedusaModule.loading_.has(hashKey)) {
|
||||||
|
return MedusaModule.loading_.get(hashKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
let finishLoading: any
|
||||||
|
let errorLoading: any
|
||||||
|
MedusaModule.loading_.set(
|
||||||
|
hashKey,
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
finishLoading = resolve
|
||||||
|
errorLoading = reject
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
let modDeclaration =
|
||||||
|
declaration ??
|
||||||
|
({} as InternalModuleDeclaration | ExternalModuleDeclaration)
|
||||||
|
|
||||||
if (declaration?.scope !== MODULE_SCOPE.EXTERNAL) {
|
if (declaration?.scope !== MODULE_SCOPE.EXTERNAL) {
|
||||||
modDeclaration = {
|
modDeclaration = {
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
resolve: defaultPath,
|
resolve: defaultPath,
|
||||||
options: declaration,
|
options: declaration,
|
||||||
|
alias: declaration?.alias,
|
||||||
|
main: declaration?.main,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,22 +176,45 @@ export class MedusaModule {
|
|||||||
moduleExports
|
moduleExports
|
||||||
)
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
await moduleLoader({
|
await moduleLoader({
|
||||||
container,
|
container,
|
||||||
moduleResolutions,
|
moduleResolutions,
|
||||||
logger,
|
logger,
|
||||||
})
|
})
|
||||||
|
} catch (err) {
|
||||||
|
errorLoading(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
const services = {}
|
const services = {}
|
||||||
|
|
||||||
for (const resolution of Object.values(moduleResolutions)) {
|
for (const resolution of Object.values(
|
||||||
|
moduleResolutions
|
||||||
|
) as ModuleResolution[]) {
|
||||||
const keyName = resolution.definition.key
|
const keyName = resolution.definition.key
|
||||||
const registrationName = resolution.definition.registrationName
|
const registrationName = resolution.definition.registrationName
|
||||||
|
|
||||||
services[keyName] = container.resolve(registrationName)
|
services[keyName] = container.resolve(registrationName)
|
||||||
|
services[keyName].__definition = resolution.definition
|
||||||
|
|
||||||
|
if (resolution.definition.isQueryable) {
|
||||||
|
services[keyName].__joinerConfig = await services[
|
||||||
|
keyName
|
||||||
|
].__joinerConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
MedusaModule.registerModule(keyName, {
|
||||||
|
key: keyName,
|
||||||
|
hash: hashKey,
|
||||||
|
alias: modDeclaration.alias ?? hashKey,
|
||||||
|
main: !!modDeclaration.main,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
MedusaModule.instances_.set(hashKey, services)
|
MedusaModule.instances_.set(hashKey, services)
|
||||||
|
finishLoading(services)
|
||||||
|
MedusaModule.loading_.delete(hashKey)
|
||||||
|
|
||||||
return services
|
return services
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
import { RemoteJoiner } from "@medusajs/orchestration"
|
||||||
|
import {
|
||||||
|
JoinerRelationship,
|
||||||
|
JoinerServiceConfig,
|
||||||
|
ModuleDefinition,
|
||||||
|
RemoteExpandProperty,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { toPascalCase } from "@medusajs/utils"
|
||||||
|
import { MedusaModule } from "./medusa-module"
|
||||||
|
|
||||||
|
export class RemoteQuery {
|
||||||
|
private remoteJoiner: RemoteJoiner
|
||||||
|
private modulesMap: Map<string, any> = new Map()
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
modulesLoaded?: (any & {
|
||||||
|
__joinerConfig: JoinerServiceConfig
|
||||||
|
__definition: ModuleDefinition
|
||||||
|
})[],
|
||||||
|
remoteFetchData?: (
|
||||||
|
expand: RemoteExpandProperty,
|
||||||
|
keyField: string,
|
||||||
|
ids?: (unknown | unknown[])[],
|
||||||
|
relationship?: JoinerRelationship
|
||||||
|
) => Promise<{
|
||||||
|
data: unknown[] | { [path: string]: unknown[] }
|
||||||
|
path?: string
|
||||||
|
}>
|
||||||
|
) {
|
||||||
|
if (!modulesLoaded?.length) {
|
||||||
|
modulesLoaded = [...MedusaModule.getLoadedModules().entries()].map(
|
||||||
|
([, mod]) => mod
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const servicesConfig: JoinerServiceConfig[] = []
|
||||||
|
for (const modService of modulesLoaded) {
|
||||||
|
const mod: any = Object.values(modService)[0]
|
||||||
|
|
||||||
|
if (!mod.__definition.isQueryable) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.modulesMap.has(mod.__definition.key)) {
|
||||||
|
throw new Error(
|
||||||
|
`Duplicated instance of module ${mod.__definition.key} is not allowed.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.modulesMap.set(mod.__definition.key, mod)
|
||||||
|
servicesConfig.push(mod.__joinerConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.remoteJoiner = new RemoteJoiner(
|
||||||
|
servicesConfig,
|
||||||
|
remoteFetchData ?? this.remoteFetchData.bind(this)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public setFetchDataCallback(
|
||||||
|
remoteFetchData: (
|
||||||
|
expand: RemoteExpandProperty,
|
||||||
|
keyField: string,
|
||||||
|
ids?: (unknown | unknown[])[],
|
||||||
|
relationship?: any
|
||||||
|
) => Promise<{
|
||||||
|
data: unknown[] | { [path: string]: unknown[] }
|
||||||
|
path?: string
|
||||||
|
}>
|
||||||
|
): void {
|
||||||
|
this.remoteJoiner.setFetchDataCallback(remoteFetchData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getAllFieldsAndRelations(
|
||||||
|
data: any,
|
||||||
|
prefix = ""
|
||||||
|
): { select: string[]; relations: string[] } {
|
||||||
|
let fields: Set<string> = new Set()
|
||||||
|
let relations: string[] = []
|
||||||
|
|
||||||
|
data.fields?.forEach((field: string) => {
|
||||||
|
fields.add(prefix ? `${prefix}.${field}` : field)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (data.expands) {
|
||||||
|
for (const property in data.expands) {
|
||||||
|
const newPrefix = prefix ? `${prefix}.${property}` : property
|
||||||
|
|
||||||
|
relations.push(newPrefix)
|
||||||
|
fields.delete(newPrefix)
|
||||||
|
|
||||||
|
const result = RemoteQuery.getAllFieldsAndRelations(
|
||||||
|
data.expands[property],
|
||||||
|
newPrefix
|
||||||
|
)
|
||||||
|
|
||||||
|
result.select.forEach(fields.add, fields)
|
||||||
|
relations = relations.concat(result.relations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { select: [...fields], relations }
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasPagination(options: { [attr: string]: unknown }): boolean {
|
||||||
|
if (!options) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrs = ["skip", "cursor"]
|
||||||
|
return Object.keys(options).some((key) => attrs.includes(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildPagination(options, count) {
|
||||||
|
return {
|
||||||
|
skip: options.skip,
|
||||||
|
take: options.take,
|
||||||
|
cursor: options.cursor,
|
||||||
|
// TODO: next cursor
|
||||||
|
count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async remoteFetchData(
|
||||||
|
expand: RemoteExpandProperty,
|
||||||
|
keyField: string,
|
||||||
|
ids?: (unknown | unknown[])[],
|
||||||
|
relationship?: JoinerRelationship
|
||||||
|
): Promise<{
|
||||||
|
data: unknown[] | { [path: string]: unknown }
|
||||||
|
path?: string
|
||||||
|
}> {
|
||||||
|
const serviceConfig = expand.serviceConfig
|
||||||
|
const service = this.modulesMap.get(serviceConfig.serviceName)
|
||||||
|
|
||||||
|
let filters = {}
|
||||||
|
const options = {
|
||||||
|
...RemoteQuery.getAllFieldsAndRelations(expand),
|
||||||
|
}
|
||||||
|
|
||||||
|
const availableOptions = [
|
||||||
|
"skip",
|
||||||
|
"take",
|
||||||
|
"limit",
|
||||||
|
"offset",
|
||||||
|
"cursor",
|
||||||
|
"sort",
|
||||||
|
]
|
||||||
|
const availableOptionsAlias = new Map([
|
||||||
|
["limit", "take"],
|
||||||
|
["offset", "skip"],
|
||||||
|
])
|
||||||
|
|
||||||
|
for (const arg of expand.args || []) {
|
||||||
|
if (arg.name === "filters" && arg.value) {
|
||||||
|
filters = { ...arg.value }
|
||||||
|
} else if (availableOptions.includes(arg.name)) {
|
||||||
|
const argName = availableOptionsAlias.has(arg.name)
|
||||||
|
? availableOptionsAlias.get(arg.name)!
|
||||||
|
: arg.name
|
||||||
|
options[argName] = arg.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ids) {
|
||||||
|
filters[keyField] = ids
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasPagination = this.hasPagination(options)
|
||||||
|
|
||||||
|
let methodName = hasPagination ? "listAndCount" : "list"
|
||||||
|
|
||||||
|
if (relationship?.args?.methodSuffix) {
|
||||||
|
methodName += toPascalCase(relationship.args.methodSuffix)
|
||||||
|
} else if (serviceConfig?.args?.methodSuffix) {
|
||||||
|
methodName += toPascalCase(serviceConfig.args.methodSuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof service[methodName] !== "function") {
|
||||||
|
throw new Error(
|
||||||
|
`Method "${methodName}" does not exist on "${serviceConfig.serviceName}"`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await service[methodName](filters, options)
|
||||||
|
|
||||||
|
if (hasPagination) {
|
||||||
|
const [data, count] = result
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
rows: data,
|
||||||
|
metadata: this.buildPagination(options, count),
|
||||||
|
},
|
||||||
|
path: "rows",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async query(query: string, variables: any = {}): Promise<any> {
|
||||||
|
return await this.remoteJoiner.query(
|
||||||
|
RemoteJoiner.parseQuery(query, variables)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"author": "Medusa",
|
"author": "Medusa",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@medusajs/types": "^1.8.10",
|
"@medusajs/types": "^1.8.11",
|
||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
"ts-jest": "^25.5.1",
|
"ts-jest": "^25.5.1",
|
||||||
|
|||||||
@@ -3,22 +3,36 @@ import { remoteJoinerData } from "./../../__fixtures__/joiner/data"
|
|||||||
|
|
||||||
export const serviceConfigs: JoinerServiceConfig[] = [
|
export const serviceConfigs: JoinerServiceConfig[] = [
|
||||||
{
|
{
|
||||||
serviceName: "User",
|
serviceName: "user",
|
||||||
primaryKeys: ["id"],
|
primaryKeys: ["id"],
|
||||||
|
args: {
|
||||||
|
methodSuffix: "User",
|
||||||
|
},
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "me",
|
||||||
|
args: {
|
||||||
|
extraArgument: 123,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "customer",
|
||||||
|
},
|
||||||
|
],
|
||||||
relationships: [
|
relationships: [
|
||||||
{
|
{
|
||||||
foreignKey: "products.product_id",
|
foreignKey: "products.product_id",
|
||||||
serviceName: "Product",
|
serviceName: "product",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
alias: "product",
|
alias: "product",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
extends: [
|
extends: [
|
||||||
{
|
{
|
||||||
serviceName: "Variant",
|
serviceName: "variantService",
|
||||||
resolve: {
|
relationship: {
|
||||||
foreignKey: "user_id",
|
foreignKey: "user_id",
|
||||||
serviceName: "User",
|
serviceName: "user",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
alias: "user",
|
alias: "user",
|
||||||
},
|
},
|
||||||
@@ -26,55 +40,58 @@ export const serviceConfigs: JoinerServiceConfig[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
serviceName: "Product",
|
serviceName: "product",
|
||||||
primaryKeys: ["id", "sku"],
|
primaryKeys: ["id", "sku"],
|
||||||
relationships: [
|
relationships: [
|
||||||
{
|
{
|
||||||
foreignKey: "user_id",
|
foreignKey: "user_id",
|
||||||
serviceName: "User",
|
serviceName: "user",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
alias: "user",
|
alias: "user",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
serviceName: "Variant",
|
serviceName: "variantService",
|
||||||
|
alias: {
|
||||||
|
name: "variant",
|
||||||
|
},
|
||||||
primaryKeys: ["id"],
|
primaryKeys: ["id"],
|
||||||
relationships: [
|
relationships: [
|
||||||
{
|
{
|
||||||
foreignKey: "product_id",
|
foreignKey: "product_id",
|
||||||
serviceName: "Product",
|
serviceName: "product",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
alias: "product",
|
alias: "product",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
foreignKey: "variant_id",
|
foreignKey: "variant_id",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
serviceName: "Order",
|
serviceName: "order",
|
||||||
alias: "orders",
|
alias: "orders",
|
||||||
inverse: true, // In an inverted relationship the foreign key is on Order and the primary key is on variant
|
inverse: true, // In an inverted relationship the foreign key is on Order and the primary key is on variant
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
serviceName: "Order",
|
serviceName: "order",
|
||||||
primaryKeys: ["id"],
|
primaryKeys: ["id"],
|
||||||
relationships: [
|
relationships: [
|
||||||
{
|
{
|
||||||
foreignKey: "product_id",
|
foreignKey: "product_id",
|
||||||
serviceName: "Product",
|
serviceName: "product",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
alias: "product",
|
alias: "product",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
foreignKey: "products.variant_id,product_id",
|
foreignKey: "products.variant_id,product_id",
|
||||||
serviceName: "Variant",
|
serviceName: "variantService",
|
||||||
primaryKey: "id,product_id",
|
primaryKey: "id,product_id",
|
||||||
alias: "variant",
|
alias: "variant",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
foreignKey: "user_id",
|
foreignKey: "user_id",
|
||||||
serviceName: "User",
|
serviceName: "user",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
alias: "user",
|
alias: "user",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ describe("RemoteJoiner.parseQuery", () => {
|
|||||||
const rjQuery = parser.parseQuery()
|
const rjQuery = parser.parseQuery()
|
||||||
|
|
||||||
expect(rjQuery).toEqual({
|
expect(rjQuery).toEqual({
|
||||||
service: "order",
|
alias: "order",
|
||||||
fields: ["id", "number", "date"],
|
fields: ["id", "number", "date"],
|
||||||
expands: [],
|
expands: [],
|
||||||
})
|
})
|
||||||
@@ -50,7 +50,7 @@ describe("RemoteJoiner.parseQuery", () => {
|
|||||||
const rjQuery = parser.parseQuery()
|
const rjQuery = parser.parseQuery()
|
||||||
|
|
||||||
expect(rjQuery).toEqual({
|
expect(rjQuery).toEqual({
|
||||||
service: "order",
|
alias: "order",
|
||||||
fields: ["id", "number", "date"],
|
fields: ["id", "number", "date"],
|
||||||
expands: [],
|
expands: [],
|
||||||
args: [
|
args: [
|
||||||
@@ -77,6 +77,44 @@ describe("RemoteJoiner.parseQuery", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("Simple query with mapping fields to services", async () => {
|
||||||
|
const graphqlQuery = `
|
||||||
|
query {
|
||||||
|
order {
|
||||||
|
id
|
||||||
|
number
|
||||||
|
date
|
||||||
|
products {
|
||||||
|
product_id
|
||||||
|
variant_id
|
||||||
|
order
|
||||||
|
variant {
|
||||||
|
name
|
||||||
|
sku
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const parser = new GraphQLParser(graphqlQuery, {})
|
||||||
|
const rjQuery = parser.parseQuery()
|
||||||
|
|
||||||
|
expect(rjQuery).toEqual({
|
||||||
|
alias: "order",
|
||||||
|
fields: ["id", "number", "date", "products"],
|
||||||
|
expands: [
|
||||||
|
{
|
||||||
|
property: "products",
|
||||||
|
fields: ["product_id", "variant_id", "order", "variant"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: "products.variant",
|
||||||
|
fields: ["name", "sku"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("Nested query with fields", async () => {
|
it("Nested query with fields", async () => {
|
||||||
const graphqlQuery = `
|
const graphqlQuery = `
|
||||||
query {
|
query {
|
||||||
@@ -100,7 +138,7 @@ describe("RemoteJoiner.parseQuery", () => {
|
|||||||
const rjQuery = parser.parseQuery()
|
const rjQuery = parser.parseQuery()
|
||||||
|
|
||||||
expect(rjQuery).toEqual({
|
expect(rjQuery).toEqual({
|
||||||
service: "order",
|
alias: "order",
|
||||||
fields: ["id", "number", "date", "products"],
|
fields: ["id", "number", "date", "products"],
|
||||||
expands: [
|
expands: [
|
||||||
{
|
{
|
||||||
@@ -138,7 +176,7 @@ describe("RemoteJoiner.parseQuery", () => {
|
|||||||
const rjQuery = parser.parseQuery()
|
const rjQuery = parser.parseQuery()
|
||||||
|
|
||||||
expect(rjQuery).toEqual({
|
expect(rjQuery).toEqual({
|
||||||
service: "order",
|
alias: "order",
|
||||||
fields: ["id", "number", "date", "products"],
|
fields: ["id", "number", "date", "products"],
|
||||||
expands: [
|
expands: [
|
||||||
{
|
{
|
||||||
@@ -205,7 +243,7 @@ describe("RemoteJoiner.parseQuery", () => {
|
|||||||
const rjQuery = parser.parseQuery()
|
const rjQuery = parser.parseQuery()
|
||||||
|
|
||||||
expect(rjQuery).toEqual({
|
expect(rjQuery).toEqual({
|
||||||
service: "order",
|
alias: "order",
|
||||||
fields: ["id", "number", "date", "products"],
|
fields: ["id", "number", "date", "products"],
|
||||||
expands: [
|
expands: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ const fetchServiceDataCallback = async (
|
|||||||
relationship?: any
|
relationship?: any
|
||||||
) => {
|
) => {
|
||||||
const serviceConfig = expand.serviceConfig
|
const serviceConfig = expand.serviceConfig
|
||||||
const moduleRegistryName =
|
const moduleRegistryName = !serviceConfig.serviceName.endsWith("Service")
|
||||||
lowerCaseFirst(serviceConfig.serviceName) + "Service"
|
? lowerCaseFirst(serviceConfig.serviceName) + "Service"
|
||||||
|
: serviceConfig.serviceName
|
||||||
|
|
||||||
const service = container.resolve(moduleRegistryName)
|
const service = container.resolve(moduleRegistryName)
|
||||||
const methodName = relationship?.inverse
|
const methodName = relationship?.inverse
|
||||||
@@ -74,7 +75,7 @@ describe("RemoteJoiner", () => {
|
|||||||
|
|
||||||
it("Simple query of a service, its id and no fields specified", async () => {
|
it("Simple query of a service, its id and no fields specified", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
service: "User",
|
service: "user",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "id",
|
name: "id",
|
||||||
@@ -143,7 +144,7 @@ describe("RemoteJoiner", () => {
|
|||||||
|
|
||||||
it("Query of a service, expanding a property and restricting the fields expanded", async () => {
|
it("Query of a service, expanding a property and restricting the fields expanded", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
service: "User",
|
service: "user",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "id",
|
name: "id",
|
||||||
@@ -215,7 +216,7 @@ describe("RemoteJoiner", () => {
|
|||||||
|
|
||||||
it("Query a service expanding multiple nested properties", async () => {
|
it("Query a service expanding multiple nested properties", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
service: "Order",
|
service: "order",
|
||||||
fields: ["number", "date", "products"],
|
fields: ["number", "date", "products"],
|
||||||
expands: [
|
expands: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ describe("RemoteJoiner", () => {
|
|||||||
|
|
||||||
it("Simple query of a service, its id and no fields specified", async () => {
|
it("Simple query of a service, its id and no fields specified", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
service: "User",
|
service: "user",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "id",
|
name: "id",
|
||||||
@@ -69,20 +69,62 @@ describe("RemoteJoiner", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Transforms main service name into PascalCase", async () => {
|
it("Simple query of a service by its alias", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
service: "user",
|
alias: "customer",
|
||||||
fields: ["id"],
|
fields: ["id"],
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
value: "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
await joiner.query(query)
|
await joiner.query(query)
|
||||||
|
|
||||||
expect(serviceMock.userService).toHaveBeenCalledTimes(1)
|
expect(serviceMock.userService).toHaveBeenCalledTimes(1)
|
||||||
|
expect(serviceMock.userService).toHaveBeenCalledWith({
|
||||||
|
args: [],
|
||||||
|
fields: ["id"],
|
||||||
|
options: { id: ["1"] },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Simple query of a service by its alias with extra arguments", async () => {
|
||||||
|
const query = {
|
||||||
|
alias: "me",
|
||||||
|
fields: ["id"],
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "arg1",
|
||||||
|
value: "abc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await joiner.query(query)
|
||||||
|
|
||||||
|
expect(serviceMock.userService).toHaveBeenCalledTimes(1)
|
||||||
|
expect(serviceMock.userService).toHaveBeenCalledWith({
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "arg1",
|
||||||
|
value: "abc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fields: ["id"],
|
||||||
|
options: { id: [1] },
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Simple query of a service, its id and a few fields specified", async () => {
|
it("Simple query of a service, its id and a few fields specified", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
service: "User",
|
service: "user",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "id",
|
name: "id",
|
||||||
@@ -148,7 +190,7 @@ describe("RemoteJoiner", () => {
|
|||||||
|
|
||||||
it("Query a service using more than 1 argument, expanding a property with another argument", async () => {
|
it("Query a service using more than 1 argument, expanding a property with another argument", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
service: "User",
|
service: "user",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "id",
|
name: "id",
|
||||||
@@ -213,7 +255,7 @@ describe("RemoteJoiner", () => {
|
|||||||
|
|
||||||
it("Query a service expanding multiple nested properties", async () => {
|
it("Query a service expanding multiple nested properties", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
service: "Order",
|
service: "order",
|
||||||
fields: ["number", "date", "products"],
|
fields: ["number", "date", "products"],
|
||||||
expands: [
|
expands: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -86,10 +86,12 @@ class GraphQLParser {
|
|||||||
if (selection.kind === "Field") {
|
if (selection.kind === "Field") {
|
||||||
const fieldNode = selection as FieldNode
|
const fieldNode = selection as FieldNode
|
||||||
|
|
||||||
if (fieldNode.selectionSet) {
|
if (!fieldNode.selectionSet) {
|
||||||
const entityName = parentName
|
return
|
||||||
? `${parentName}.${fieldNode.name.value}`
|
}
|
||||||
: fieldNode.name.value
|
|
||||||
|
const propName = fieldNode.name.value
|
||||||
|
const entityName = parentName ? `${parentName}.${propName}` : propName
|
||||||
|
|
||||||
const nestedEntity: Entity = {
|
const nestedEntity: Entity = {
|
||||||
property: entityName.replace(`${mainService}.`, ""),
|
property: entityName.replace(`${mainService}.`, ""),
|
||||||
@@ -108,7 +110,6 @@ class GraphQLParser {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return entities
|
return entities
|
||||||
@@ -126,8 +127,9 @@ class GraphQLParser {
|
|||||||
const rootFieldNode = queryDefinition.selectionSet
|
const rootFieldNode = queryDefinition.selectionSet
|
||||||
.selections[0] as FieldNode
|
.selections[0] as FieldNode
|
||||||
|
|
||||||
|
const propName = rootFieldNode.name.value
|
||||||
const remoteJoinConfig: RemoteJoinerQuery = {
|
const remoteJoinConfig: RemoteJoinerQuery = {
|
||||||
service: rootFieldNode.name.value,
|
alias: propName,
|
||||||
fields: [],
|
fields: [],
|
||||||
expands: [],
|
expands: [],
|
||||||
}
|
}
|
||||||
@@ -140,10 +142,11 @@ class GraphQLParser {
|
|||||||
remoteJoinConfig.fields = rootFieldNode.selectionSet.selections.map(
|
remoteJoinConfig.fields = rootFieldNode.selectionSet.selections.map(
|
||||||
(field) => (field as FieldNode).name.value
|
(field) => (field as FieldNode).name.value
|
||||||
)
|
)
|
||||||
|
|
||||||
remoteJoinConfig.expands = this.extractEntities(
|
remoteJoinConfig.expands = this.extractEntities(
|
||||||
rootFieldNode.selectionSet,
|
rootFieldNode.selectionSet,
|
||||||
rootFieldNode.name.value,
|
propName,
|
||||||
rootFieldNode.name.value
|
propName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
JoinerRelationship,
|
JoinerRelationship,
|
||||||
JoinerServiceConfig,
|
JoinerServiceConfig,
|
||||||
|
JoinerServiceConfigAlias,
|
||||||
RemoteExpandProperty,
|
RemoteExpandProperty,
|
||||||
RemoteJoinerQuery,
|
RemoteJoinerQuery,
|
||||||
RemoteNestedExpands,
|
RemoteNestedExpands,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { isDefined, toPascalCase } from "@medusajs/utils"
|
import { isDefined } from "@medusajs/utils"
|
||||||
import GraphQLParser from "./graphql-ast"
|
import GraphQLParser from "./graphql-ast"
|
||||||
|
|
||||||
const BASE_PATH = "_root"
|
const BASE_PATH = "_root"
|
||||||
export class RemoteJoiner {
|
export class RemoteJoiner {
|
||||||
private serviceConfigs: JoinerServiceConfig[]
|
|
||||||
private serviceConfigCache: Map<string, JoinerServiceConfig> = new Map()
|
private serviceConfigCache: Map<string, JoinerServiceConfig> = new Map()
|
||||||
|
|
||||||
private static filterFields(
|
private static filterFields(
|
||||||
@@ -84,37 +84,77 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
serviceConfigs: JoinerServiceConfig[],
|
private serviceConfigs: JoinerServiceConfig[],
|
||||||
private remoteFetchData: (
|
private remoteFetchData: (
|
||||||
expand: RemoteExpandProperty,
|
expand: RemoteExpandProperty,
|
||||||
pkField: string,
|
keyField: string,
|
||||||
ids?: (unknown | unknown[])[],
|
ids?: (unknown | unknown[])[],
|
||||||
relationship?: any
|
relationship?: any
|
||||||
) => Promise<{
|
) => Promise<{
|
||||||
data: unknown[] | { [path: string]: unknown[] }
|
data: unknown[] | { [path: string]: unknown }
|
||||||
path?: string
|
path?: string
|
||||||
}>
|
}>
|
||||||
) {
|
) {
|
||||||
this.serviceConfigs = this.buildReferences(serviceConfigs)
|
this.serviceConfigs = this.buildReferences(serviceConfigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setFetchDataCallback(
|
||||||
|
remoteFetchData: (
|
||||||
|
expand: RemoteExpandProperty,
|
||||||
|
keyField: string,
|
||||||
|
ids?: (unknown | unknown[])[],
|
||||||
|
relationship?: any
|
||||||
|
) => Promise<{
|
||||||
|
data: unknown[] | { [path: string]: unknown }
|
||||||
|
path?: string
|
||||||
|
}>
|
||||||
|
): void {
|
||||||
|
this.remoteFetchData = remoteFetchData
|
||||||
|
}
|
||||||
|
|
||||||
private buildReferences(serviceConfigs: JoinerServiceConfig[]) {
|
private buildReferences(serviceConfigs: JoinerServiceConfig[]) {
|
||||||
const expandedRelationships: Map<string, JoinerRelationship[]> = new Map()
|
const expandedRelationships: Map<string, JoinerRelationship[]> = new Map()
|
||||||
for (const service of serviceConfigs) {
|
for (const service of serviceConfigs) {
|
||||||
// self-reference
|
if (this.serviceConfigCache.has(service.serviceName)) {
|
||||||
const propName = service.serviceName.toLowerCase()
|
throw new Error(`Service "${service.serviceName}" is already defined.`)
|
||||||
|
}
|
||||||
|
|
||||||
if (!service.relationships) {
|
if (!service.relationships) {
|
||||||
service.relationships = []
|
service.relationships = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add aliases
|
||||||
|
if (!service.alias) {
|
||||||
|
service.alias = [{ name: service.serviceName.toLowerCase() }]
|
||||||
|
} else if (!Array.isArray(service.alias)) {
|
||||||
|
service.alias = [service.alias]
|
||||||
|
}
|
||||||
|
|
||||||
|
// self-reference
|
||||||
|
for (const alias of service.alias) {
|
||||||
|
if (this.serviceConfigCache.has(`alias_${alias.name}}`)) {
|
||||||
|
const defined = this.serviceConfigCache.get(`alias_${alias.name}}`)
|
||||||
|
throw new Error(
|
||||||
|
`Cannot add alias "${alias.name}" for "${service.serviceName}". It is already defined for Service "${defined?.serviceName}".`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const args =
|
||||||
|
service.args || alias.args
|
||||||
|
? { ...service.args, ...alias.args }
|
||||||
|
: undefined
|
||||||
|
|
||||||
service.relationships?.push({
|
service.relationships?.push({
|
||||||
alias: propName,
|
alias: alias.name,
|
||||||
foreignKey: propName + "_id",
|
foreignKey: alias.name + "_id",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
serviceName: service.serviceName,
|
serviceName: service.serviceName,
|
||||||
|
args,
|
||||||
})
|
})
|
||||||
|
this.cacheServiceConfig(serviceConfigs, undefined, alias.name)
|
||||||
|
}
|
||||||
|
|
||||||
this.serviceConfigCache.set(service.serviceName, service)
|
this.cacheServiceConfig(serviceConfigs, service.serviceName)
|
||||||
|
|
||||||
if (!service.extends) {
|
if (!service.extends) {
|
||||||
continue
|
continue
|
||||||
@@ -125,13 +165,13 @@ export class RemoteJoiner {
|
|||||||
expandedRelationships.set(extend.serviceName, [])
|
expandedRelationships.set(extend.serviceName, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
expandedRelationships.get(extend.serviceName)!.push(extend.resolve)
|
expandedRelationships.get(extend.serviceName)!.push(extend.relationship)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [serviceName, relationships] of expandedRelationships) {
|
for (const [serviceName, relationships] of expandedRelationships) {
|
||||||
if (!this.serviceConfigCache.has(serviceName)) {
|
if (!this.serviceConfigCache.has(serviceName)) {
|
||||||
throw new Error(`Service ${serviceName} not found`)
|
throw new Error(`Service "${serviceName}" was not found`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const service = this.serviceConfigCache.get(serviceName)
|
const service = this.serviceConfigCache.get(serviceName)
|
||||||
@@ -141,16 +181,49 @@ export class RemoteJoiner {
|
|||||||
return serviceConfigs
|
return serviceConfigs
|
||||||
}
|
}
|
||||||
|
|
||||||
private findServiceConfig(
|
private getServiceConfig(
|
||||||
serviceName: string
|
serviceName?: string,
|
||||||
|
serviceAlias?: string
|
||||||
): JoinerServiceConfig | undefined {
|
): JoinerServiceConfig | undefined {
|
||||||
if (!this.serviceConfigCache.has(serviceName)) {
|
if (serviceAlias) {
|
||||||
const config = this.serviceConfigs.find(
|
const name = `alias_${serviceAlias}`
|
||||||
|
return this.serviceConfigCache.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.serviceConfigCache.get(serviceName!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private cacheServiceConfig(
|
||||||
|
serviceConfigs,
|
||||||
|
serviceName?: string,
|
||||||
|
serviceAlias?: string
|
||||||
|
): void {
|
||||||
|
if (serviceAlias) {
|
||||||
|
const name = `alias_${serviceAlias}`
|
||||||
|
if (!this.serviceConfigCache.has(name)) {
|
||||||
|
let aliasConfig: JoinerServiceConfigAlias | undefined
|
||||||
|
const config = serviceConfigs.find((conf) => {
|
||||||
|
const aliases = conf.alias as JoinerServiceConfigAlias[]
|
||||||
|
const hasArgs = aliases?.find((alias) => alias.name === serviceAlias)
|
||||||
|
aliasConfig = hasArgs
|
||||||
|
return hasArgs
|
||||||
|
})
|
||||||
|
|
||||||
|
if (config) {
|
||||||
|
const serviceConfig = { ...config }
|
||||||
|
if (aliasConfig) {
|
||||||
|
serviceConfig.args = { ...config?.args, ...aliasConfig?.args }
|
||||||
|
}
|
||||||
|
this.serviceConfigCache.set(name, serviceConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = serviceConfigs.find(
|
||||||
(config) => config.serviceName === serviceName
|
(config) => config.serviceName === serviceName
|
||||||
)
|
)
|
||||||
this.serviceConfigCache.set(serviceName, config!)
|
this.serviceConfigCache.set(serviceName!, config)
|
||||||
}
|
|
||||||
return this.serviceConfigCache.get(serviceName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchData(
|
private async fetchData(
|
||||||
@@ -159,7 +232,7 @@ export class RemoteJoiner {
|
|||||||
ids?: (unknown | unknown[])[],
|
ids?: (unknown | unknown[])[],
|
||||||
relationship?: any
|
relationship?: any
|
||||||
): Promise<{
|
): Promise<{
|
||||||
data: unknown[] | { [path: string]: unknown[] }
|
data: unknown[] | { [path: string]: unknown }
|
||||||
path?: string
|
path?: string
|
||||||
}> {
|
}> {
|
||||||
let uniqueIds = Array.isArray(ids) ? ids : ids ? [ids] : undefined
|
let uniqueIds = Array.isArray(ids) ? ids : ids ? [ids] : undefined
|
||||||
@@ -218,7 +291,7 @@ export class RemoteJoiner {
|
|||||||
|
|
||||||
const stack: [
|
const stack: [
|
||||||
any[],
|
any[],
|
||||||
RemoteJoinerQuery,
|
Partial<RemoteJoinerQuery>,
|
||||||
Map<string, RemoteExpandProperty>,
|
Map<string, RemoteExpandProperty>,
|
||||||
string,
|
string,
|
||||||
Set<string>
|
Set<string>
|
||||||
@@ -245,23 +318,25 @@ export class RemoteJoiner {
|
|||||||
resolvedPaths.add(expandedPath)
|
resolvedPaths.add(expandedPath)
|
||||||
|
|
||||||
const property = expand.property || ""
|
const property = expand.property || ""
|
||||||
const parentServiceConfig = this.findServiceConfig(currentQuery.service)
|
const parentServiceConfig = this.getServiceConfig(
|
||||||
|
currentQuery.service,
|
||||||
|
currentQuery.alias
|
||||||
|
)
|
||||||
|
|
||||||
await this.expandProperty(currentItems, parentServiceConfig!, expand)
|
await this.expandProperty(currentItems, parentServiceConfig!, expand)
|
||||||
|
|
||||||
const relationship = parentServiceConfig?.relationships?.find(
|
|
||||||
(relation) => relation.alias === property
|
|
||||||
)
|
|
||||||
|
|
||||||
const nestedItems = RemoteJoiner.getNestedItems(currentItems, property)
|
const nestedItems = RemoteJoiner.getNestedItems(currentItems, property)
|
||||||
|
|
||||||
if (nestedItems.length > 0) {
|
if (nestedItems.length > 0) {
|
||||||
const nextProp = relationship
|
const relationship = expand.serviceConfig
|
||||||
? {
|
|
||||||
...currentQuery,
|
let nextProp = currentQuery
|
||||||
|
if (relationship) {
|
||||||
|
const relQuery = {
|
||||||
service: relationship.serviceName,
|
service: relationship.serviceName,
|
||||||
}
|
}
|
||||||
: currentQuery
|
nextProp = relQuery
|
||||||
|
}
|
||||||
|
|
||||||
stack.push([
|
stack.push([
|
||||||
nestedItems,
|
nestedItems,
|
||||||
@@ -356,9 +431,19 @@ export class RemoteJoiner {
|
|||||||
|
|
||||||
if (Array.isArray(item[field])) {
|
if (Array.isArray(item[field])) {
|
||||||
item[relationship.alias] = item[field]
|
item[relationship.alias] = item[field]
|
||||||
.map((id) => relatedDataMap[id])
|
.map((id) => {
|
||||||
|
if (relationship.isList && !Array.isArray(relatedDataMap[id])) {
|
||||||
|
relatedDataMap[id] = [relatedDataMap[id]]
|
||||||
|
}
|
||||||
|
|
||||||
|
return relatedDataMap[id]
|
||||||
|
})
|
||||||
.filter((relatedItem) => relatedItem !== undefined)
|
.filter((relatedItem) => relatedItem !== undefined)
|
||||||
} else {
|
} else {
|
||||||
|
if (relationship.isList && !Array.isArray(relatedDataMap[itemKey])) {
|
||||||
|
relatedDataMap[itemKey] = [relatedDataMap[itemKey]]
|
||||||
|
}
|
||||||
|
|
||||||
item[relationship.alias] = relatedDataMap[itemKey]
|
item[relationship.alias] = relatedDataMap[itemKey]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,9 +524,7 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentServiceConfig = this.findServiceConfig(
|
currentServiceConfig = this.getServiceConfig(relationship.serviceName)
|
||||||
relationship.serviceName
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!currentServiceConfig) {
|
if (!currentServiceConfig) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -522,11 +605,17 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async query(queryObj: RemoteJoinerQuery): Promise<any> {
|
async query(queryObj: RemoteJoinerQuery): Promise<any> {
|
||||||
queryObj.service = toPascalCase(queryObj.service)
|
const serviceConfig = this.getServiceConfig(
|
||||||
const serviceConfig = this.findServiceConfig(queryObj.service)
|
queryObj.service,
|
||||||
|
queryObj.alias
|
||||||
|
)
|
||||||
|
|
||||||
if (!serviceConfig) {
|
if (!serviceConfig) {
|
||||||
throw new Error(`Service not found: ${queryObj.service}`)
|
if (queryObj.alias) {
|
||||||
|
throw new Error(`Service with alias "${queryObj.alias}" was not found.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Service "${queryObj.service}" was not found.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
let pkName = serviceConfig.primaryKeys[0]
|
let pkName = serviceConfig.primaryKeys[0]
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
|
||||||
import { ProductCategoryService } from "@services"
|
|
||||||
import { ProductCategoryRepository } from "@repositories"
|
|
||||||
import { ProductCategory } from "@models"
|
import { ProductCategory } from "@models"
|
||||||
|
import { ProductCategoryRepository } from "@repositories"
|
||||||
|
import { ProductCategoryService } from "@services"
|
||||||
|
|
||||||
import { TestDatabase } from "../../../utils"
|
|
||||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||||
import { productCategoriesData } from "../../../__fixtures__/product-category/data"
|
import { productCategoriesData } from "../../../__fixtures__/product-category/data"
|
||||||
|
import { TestDatabase } from "../../../utils"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
|
||||||
import { ProductCollection } from "@models"
|
import { ProductCollection } from "@models"
|
||||||
import { ProductCollectionService } from "@services"
|
|
||||||
import { ProductCollectionRepository } from "@repositories"
|
import { ProductCollectionRepository } from "@repositories"
|
||||||
|
import { ProductCollectionService } from "@services"
|
||||||
|
|
||||||
import { TestDatabase } from "../../../utils"
|
|
||||||
import { createCollections } from "../../../__fixtures__/product"
|
import { createCollections } from "../../../__fixtures__/product"
|
||||||
|
import { TestDatabase } from "../../../utils"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
|
||||||
import { ProductTagService } from "@services"
|
|
||||||
import { ProductTagRepository } from "@repositories"
|
|
||||||
import { Product } from "@models"
|
import { Product } from "@models"
|
||||||
|
import { ProductTagRepository } from "@repositories"
|
||||||
|
import { ProductTagService } from "@services"
|
||||||
|
|
||||||
import { TestDatabase } from "../../../utils"
|
|
||||||
import { createProductAndTags } from "../../../__fixtures__/product"
|
|
||||||
import { ProductTypes } from "@medusajs/types"
|
import { ProductTypes } from "@medusajs/types"
|
||||||
|
import { createProductAndTags } from "../../../__fixtures__/product"
|
||||||
|
import { TestDatabase } from "../../../utils"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
|||||||
@@ -280,18 +280,15 @@ describe("ProductVariant Service", () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: productVariantTestOne,
|
id: productVariantTestOne,
|
||||||
title: "variant 1",
|
title: "variant 1",
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return requested attributes when requested through config", async () => {
|
it("should return requested attributes when requested through config", async () => {
|
||||||
const result = await service.retrieve(
|
const result = await service.retrieve(variantOne.id, {
|
||||||
variantOne.id,
|
|
||||||
{
|
|
||||||
select: ["id", "title", "product.title"] as any,
|
select: ["id", "title", "product.title"] as any,
|
||||||
relations: ["product"],
|
relations: ["product"],
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
expect(result).toEqual(
|
expect(result).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -302,7 +299,7 @@ describe("ProductVariant Service", () => {
|
|||||||
id: "product-1",
|
id: "product-1",
|
||||||
title: "product 1",
|
title: "product 1",
|
||||||
}),
|
}),
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -315,7 +312,9 @@ describe("ProductVariant Service", () => {
|
|||||||
error = e
|
error = e
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toEqual("ProductVariant with id: does-not-exist was not found")
|
expect(error.message).toEqual(
|
||||||
|
"ProductVariant with id: does-not-exist was not found"
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should throw an error when an id is not provided", async () => {
|
it("should throw an error when an id is not provided", async () => {
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import { TestDatabase } from "../../../utils"
|
|
||||||
import { ProductService } from "@services"
|
|
||||||
import { ProductRepository } from "@repositories"
|
|
||||||
import { Image, Product, ProductCategory, ProductVariant } from "@models"
|
import { Image, Product, ProductCategory, ProductVariant } from "@models"
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
|
||||||
import { ProductDTO } from "@medusajs/types"
|
|
||||||
|
|
||||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
|
||||||
import {
|
import {
|
||||||
assignCategoriesToProduct,
|
assignCategoriesToProduct,
|
||||||
createImages,
|
createImages,
|
||||||
@@ -17,7 +10,14 @@ import {
|
|||||||
productsData,
|
productsData,
|
||||||
variantsData,
|
variantsData,
|
||||||
} from "../../../__fixtures__/product/data"
|
} from "../../../__fixtures__/product/data"
|
||||||
|
|
||||||
|
import { ProductDTO } from "@medusajs/types"
|
||||||
|
import { ProductRepository } from "@repositories"
|
||||||
|
import { ProductService } from "@services"
|
||||||
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { TestDatabase } from "../../../utils"
|
||||||
import { buildProductOnlyData } from "../../../__fixtures__/product/data/create-product"
|
import { buildProductOnlyData } from "../../../__fixtures__/product/data/create-product"
|
||||||
|
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||||
import { kebabCase } from "@medusajs/utils"
|
import { kebabCase } from "@medusajs/utils"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { ProductServiceInitializeOptions } from "../../src/types"
|
||||||
|
|
||||||
|
export const databaseOptions: ProductServiceInitializeOptions["database"] = {
|
||||||
|
schema: "public",
|
||||||
|
clientUrl: "medusa-products-test",
|
||||||
|
}
|
||||||
@@ -37,7 +37,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mikro-orm/cli": "5.7.12",
|
"@mikro-orm/cli": "5.7.12",
|
||||||
"@mikro-orm/migrations": "5.7.12",
|
|
||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"faker": "^6.6.6",
|
"faker": "^6.6.6",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const initialize = async (
|
|||||||
): Promise<IProductModuleService> => {
|
): Promise<IProductModuleService> => {
|
||||||
const serviceKey = Modules.PRODUCT
|
const serviceKey = Modules.PRODUCT
|
||||||
|
|
||||||
const loaded = await MedusaModule.bootstrap(
|
const loaded = await MedusaModule.bootstrap<IProductModuleService>(
|
||||||
serviceKey,
|
serviceKey,
|
||||||
MODULE_PACKAGE_NAMES[Modules.PRODUCT],
|
MODULE_PACKAGE_NAMES[Modules.PRODUCT],
|
||||||
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||||
@@ -26,5 +26,5 @@ export const initialize = async (
|
|||||||
injectedDependencies
|
injectedDependencies
|
||||||
)
|
)
|
||||||
|
|
||||||
return loaded[serviceKey] as IProductModuleService
|
return loaded[serviceKey]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { JoinerServiceConfig } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const joinerConfig: JoinerServiceConfig = {
|
||||||
|
serviceName: Modules.PRODUCT,
|
||||||
|
primaryKeys: ["id", "handle"],
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "product",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "products",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "variant",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Variants",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "variants",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Variants",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_option",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Options",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_options",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Options",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_type",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Types",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_types",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Types",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_image",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Images",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_images",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Images",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_tag",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Tags",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_tags",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Tags",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_collection",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Collections",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_collections",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Collections",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_category",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Categories",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "product_categories",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Categories",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -54,7 +54,6 @@ async function loadDefault({ database, container }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||||
|
|
||||||
const orm = await createConnection(database, entities)
|
const orm = await createConnection(database, entities)
|
||||||
|
|
||||||
container.register({
|
container.register({
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
import { LoaderOptions } from "@medusajs/modules-sdk"
|
|
||||||
|
|
||||||
import { asClass } from "awilix"
|
|
||||||
import {
|
|
||||||
ProductCategoryService,
|
|
||||||
ProductCollectionService,
|
|
||||||
ProductImageService,
|
|
||||||
ProductModuleService,
|
|
||||||
ProductOptionService,
|
|
||||||
ProductService,
|
|
||||||
ProductTagService,
|
|
||||||
ProductTypeService,
|
|
||||||
ProductVariantService,
|
|
||||||
} from "@services"
|
|
||||||
import * as DefaultRepositories from "@repositories"
|
import * as DefaultRepositories from "@repositories"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BaseRepository,
|
BaseRepository,
|
||||||
ProductCategoryRepository,
|
ProductCategoryRepository,
|
||||||
@@ -25,6 +12,20 @@ import {
|
|||||||
ProductVariantRepository,
|
ProductVariantRepository,
|
||||||
} from "@repositories"
|
} from "@repositories"
|
||||||
import { Constructor, DAL, ModulesSdkTypes } from "@medusajs/types"
|
import { Constructor, DAL, ModulesSdkTypes } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ProductCategoryService,
|
||||||
|
ProductCollectionService,
|
||||||
|
ProductImageService,
|
||||||
|
ProductModuleService,
|
||||||
|
ProductOptionService,
|
||||||
|
ProductService,
|
||||||
|
ProductTagService,
|
||||||
|
ProductTypeService,
|
||||||
|
ProductVariantService,
|
||||||
|
} from "@services"
|
||||||
|
|
||||||
|
import { LoaderOptions } from "@medusajs/modules-sdk"
|
||||||
|
import { asClass } from "awilix"
|
||||||
import { lowerCaseFirst } from "@medusajs/utils"
|
import { lowerCaseFirst } from "@medusajs/utils"
|
||||||
|
|
||||||
export default async ({
|
export default async ({
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"namespaces": [
|
"namespaces": ["public"],
|
||||||
"public"
|
|
||||||
],
|
|
||||||
"name": "public",
|
"name": "public",
|
||||||
"tables": [
|
"tables": [
|
||||||
{
|
{
|
||||||
@@ -117,27 +115,21 @@
|
|||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"keyName": "IDX_product_category_path",
|
"keyName": "IDX_product_category_path",
|
||||||
"columnNames": [
|
"columnNames": ["mpath"],
|
||||||
"mpath"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false
|
"unique": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_product_category_handle",
|
"keyName": "IDX_product_category_handle",
|
||||||
"columnNames": [
|
"columnNames": ["handle"],
|
||||||
"handle"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": true
|
"unique": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "product_category_pkey",
|
"keyName": "product_category_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -147,13 +139,9 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"product_category_parent_category_id_foreign": {
|
"product_category_parent_category_id_foreign": {
|
||||||
"constraintName": "product_category_parent_category_id_foreign",
|
"constraintName": "product_category_parent_category_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["parent_category_id"],
|
||||||
"parent_category_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_category",
|
"localTableName": "public.product_category",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product_category",
|
"referencedTableName": "public.product_category",
|
||||||
"deleteRule": "set null",
|
"deleteRule": "set null",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
@@ -213,9 +201,7 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["deleted_at"],
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_collection_deleted_at",
|
"keyName": "IDX_product_collection_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -223,18 +209,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_product_collection_handle_unique",
|
"keyName": "IDX_product_collection_handle_unique",
|
||||||
"columnNames": [
|
"columnNames": ["handle"],
|
||||||
"handle"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": true
|
"unique": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "product_collection_pkey",
|
"keyName": "product_collection_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -287,18 +269,14 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["url"],
|
||||||
"url"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_image_url",
|
"keyName": "IDX_product_image_url",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false
|
"unique": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["deleted_at"],
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_image_deleted_at",
|
"keyName": "IDX_product_image_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -306,9 +284,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "image_pkey",
|
"keyName": "image_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -361,9 +337,7 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["deleted_at"],
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_tag_deleted_at",
|
"keyName": "IDX_product_tag_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -371,9 +345,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "product_tag_pkey",
|
"keyName": "product_tag_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -426,9 +398,7 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["deleted_at"],
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_type_deleted_at",
|
"keyName": "IDX_product_type_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -436,9 +406,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "product_type_pkey",
|
"keyName": "product_type_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -511,12 +479,7 @@
|
|||||||
"autoincrement": false,
|
"autoincrement": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
"enumItems": [
|
"enumItems": ["draft", "proposed", "published", "rejected"],
|
||||||
"draft",
|
|
||||||
"proposed",
|
|
||||||
"published",
|
|
||||||
"rejected"
|
|
||||||
],
|
|
||||||
"mappedType": "enum"
|
"mappedType": "enum"
|
||||||
},
|
},
|
||||||
"thumbnail": {
|
"thumbnail": {
|
||||||
@@ -681,18 +644,14 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["type_id"],
|
||||||
"type_id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_type_id",
|
"keyName": "IDX_product_type_id",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false
|
"unique": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["deleted_at"],
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_deleted_at",
|
"keyName": "IDX_product_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -700,18 +659,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_product_handle_unique",
|
"keyName": "IDX_product_handle_unique",
|
||||||
"columnNames": [
|
"columnNames": ["handle"],
|
||||||
"handle"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": true
|
"unique": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "product_pkey",
|
"keyName": "product_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -721,26 +676,18 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"product_collection_id_foreign": {
|
"product_collection_id_foreign": {
|
||||||
"constraintName": "product_collection_id_foreign",
|
"constraintName": "product_collection_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["collection_id"],
|
||||||
"collection_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product",
|
"localTableName": "public.product",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product_collection",
|
"referencedTableName": "public.product_collection",
|
||||||
"deleteRule": "set null",
|
"deleteRule": "set null",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"product_type_id_foreign": {
|
"product_type_id_foreign": {
|
||||||
"constraintName": "product_type_id_foreign",
|
"constraintName": "product_type_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["type_id"],
|
||||||
"type_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product",
|
"localTableName": "public.product",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product_type",
|
"referencedTableName": "public.product_type",
|
||||||
"deleteRule": "set null",
|
"deleteRule": "set null",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
@@ -800,18 +747,14 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["product_id"],
|
||||||
"product_id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_option_product_id",
|
"keyName": "IDX_product_option_product_id",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false
|
"unique": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["deleted_at"],
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_option_deleted_at",
|
"keyName": "IDX_product_option_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -819,9 +762,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "product_option_pkey",
|
"keyName": "product_option_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -831,13 +772,9 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"product_option_product_id_foreign": {
|
"product_option_product_id_foreign": {
|
||||||
"constraintName": "product_option_product_id_foreign",
|
"constraintName": "product_option_product_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["product_id"],
|
||||||
"product_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_option",
|
"localTableName": "public.product_option",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product",
|
"referencedTableName": "public.product",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
@@ -869,10 +806,7 @@
|
|||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"keyName": "product_tags_pkey",
|
"keyName": "product_tags_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["product_id", "product_tag_id"],
|
||||||
"product_id",
|
|
||||||
"product_tag_id"
|
|
||||||
],
|
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -882,26 +816,18 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"product_tags_product_id_foreign": {
|
"product_tags_product_id_foreign": {
|
||||||
"constraintName": "product_tags_product_id_foreign",
|
"constraintName": "product_tags_product_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["product_id"],
|
||||||
"product_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_tags",
|
"localTableName": "public.product_tags",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product",
|
"referencedTableName": "public.product",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"product_tags_product_tag_id_foreign": {
|
"product_tags_product_tag_id_foreign": {
|
||||||
"constraintName": "product_tags_product_tag_id_foreign",
|
"constraintName": "product_tags_product_tag_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["product_tag_id"],
|
||||||
"product_tag_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_tags",
|
"localTableName": "public.product_tags",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product_tag",
|
"referencedTableName": "public.product_tag",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
@@ -934,10 +860,7 @@
|
|||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"keyName": "product_images_pkey",
|
"keyName": "product_images_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["product_id", "product_image_id"],
|
||||||
"product_id",
|
|
||||||
"product_image_id"
|
|
||||||
],
|
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -947,26 +870,18 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"product_images_product_id_foreign": {
|
"product_images_product_id_foreign": {
|
||||||
"constraintName": "product_images_product_id_foreign",
|
"constraintName": "product_images_product_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["product_id"],
|
||||||
"product_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_images",
|
"localTableName": "public.product_images",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product",
|
"referencedTableName": "public.product",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"product_images_product_image_id_foreign": {
|
"product_images_product_image_id_foreign": {
|
||||||
"constraintName": "product_images_product_image_id_foreign",
|
"constraintName": "product_images_product_image_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["product_image_id"],
|
||||||
"product_image_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_images",
|
"localTableName": "public.product_images",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.image",
|
"referencedTableName": "public.image",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
@@ -999,10 +914,7 @@
|
|||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"keyName": "product_category_product_pkey",
|
"keyName": "product_category_product_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["product_id", "product_category_id"],
|
||||||
"product_id",
|
|
||||||
"product_category_id"
|
|
||||||
],
|
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -1012,26 +924,18 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"product_category_product_product_id_foreign": {
|
"product_category_product_product_id_foreign": {
|
||||||
"constraintName": "product_category_product_product_id_foreign",
|
"constraintName": "product_category_product_product_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["product_id"],
|
||||||
"product_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_category_product",
|
"localTableName": "public.product_category_product",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product",
|
"referencedTableName": "public.product",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"product_category_product_product_category_id_foreign": {
|
"product_category_product_product_category_id_foreign": {
|
||||||
"constraintName": "product_category_product_product_category_id_foreign",
|
"constraintName": "product_category_product_product_category_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["product_category_id"],
|
||||||
"product_category_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_category_product",
|
"localTableName": "public.product_category_product",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product_category",
|
"referencedTableName": "public.product_category",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
@@ -1259,18 +1163,14 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["deleted_at"],
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_variant_deleted_at",
|
"keyName": "IDX_product_variant_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false
|
"unique": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["product_id"],
|
||||||
"product_id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_variant_product_id",
|
"keyName": "IDX_product_variant_product_id",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -1278,45 +1178,35 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_product_variant_sku_unique",
|
"keyName": "IDX_product_variant_sku_unique",
|
||||||
"columnNames": [
|
"columnNames": ["sku"],
|
||||||
"sku"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": true
|
"unique": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_product_variant_barcode_unique",
|
"keyName": "IDX_product_variant_barcode_unique",
|
||||||
"columnNames": [
|
"columnNames": ["barcode"],
|
||||||
"barcode"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": true
|
"unique": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_product_variant_ean_unique",
|
"keyName": "IDX_product_variant_ean_unique",
|
||||||
"columnNames": [
|
"columnNames": ["ean"],
|
||||||
"ean"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": true
|
"unique": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_product_variant_upc_unique",
|
"keyName": "IDX_product_variant_upc_unique",
|
||||||
"columnNames": [
|
"columnNames": ["upc"],
|
||||||
"upc"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": true
|
"unique": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "product_variant_pkey",
|
"keyName": "product_variant_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -1326,13 +1216,9 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"product_variant_product_id_foreign": {
|
"product_variant_product_id_foreign": {
|
||||||
"constraintName": "product_variant_product_id_foreign",
|
"constraintName": "product_variant_product_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["product_id"],
|
||||||
"product_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_variant",
|
"localTableName": "public.product_variant",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product",
|
"referencedTableName": "public.product",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
@@ -1401,27 +1287,21 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["option_id"],
|
||||||
"option_id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_option_value_option_id",
|
"keyName": "IDX_product_option_value_option_id",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false
|
"unique": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["variant_id"],
|
||||||
"variant_id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_option_value_variant_id",
|
"keyName": "IDX_product_option_value_variant_id",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false
|
"unique": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": ["deleted_at"],
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_product_option_value_deleted_at",
|
"keyName": "IDX_product_option_value_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -1429,9 +1309,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "product_option_value_pkey",
|
"keyName": "product_option_value_pkey",
|
||||||
"columnNames": [
|
"columnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -1441,25 +1319,17 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"product_option_value_option_id_foreign": {
|
"product_option_value_option_id_foreign": {
|
||||||
"constraintName": "product_option_value_option_id_foreign",
|
"constraintName": "product_option_value_option_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["option_id"],
|
||||||
"option_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_option_value",
|
"localTableName": "public.product_option_value",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product_option",
|
"referencedTableName": "public.product_option",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"product_option_value_variant_id_foreign": {
|
"product_option_value_variant_id_foreign": {
|
||||||
"constraintName": "product_option_value_variant_id_foreign",
|
"constraintName": "product_option_value_variant_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": ["variant_id"],
|
||||||
"variant_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.product_option_value",
|
"localTableName": "public.product_option_value",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": ["id"],
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.product_variant",
|
"referencedTableName": "public.product_variant",
|
||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import {
|
|||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { generateEntityId } from "@medusajs/utils"
|
import { ProductOption, ProductVariant } from "./index"
|
||||||
|
|
||||||
import ProductOption from "./product-option"
|
|
||||||
import { ProductVariant } from "./index"
|
|
||||||
import { SoftDeletable } from "../utils"
|
import { SoftDeletable } from "../utils"
|
||||||
|
import { generateEntityId } from "@medusajs/utils"
|
||||||
|
|
||||||
type OptionalFields =
|
type OptionalFields =
|
||||||
| "created_at"
|
| "created_at"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { generateEntityId } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
Cascade,
|
Cascade,
|
||||||
@@ -10,7 +11,6 @@ import {
|
|||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { generateEntityId } from "@medusajs/utils"
|
|
||||||
import { Product } from "./index"
|
import { Product } from "./index"
|
||||||
import ProductOptionValue from "./product-option-value"
|
import ProductOptionValue from "./product-option-value"
|
||||||
import { SoftDeletable } from "../utils"
|
import { SoftDeletable } from "../utils"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { generateEntityId } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
Cascade,
|
Cascade,
|
||||||
@@ -11,7 +12,6 @@ import {
|
|||||||
Property,
|
Property,
|
||||||
Unique,
|
Unique,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { generateEntityId } from "@medusajs/utils"
|
|
||||||
import { Product } from "@models"
|
import { Product } from "@models"
|
||||||
import ProductOptionValue from "./product-option-value"
|
import ProductOptionValue from "./product-option-value"
|
||||||
import { SoftDeletable } from "../utils"
|
import { SoftDeletable } from "../utils"
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
import * as ProductModels from "@models"
|
||||||
|
|
||||||
import { ModuleExports } from "@medusajs/types"
|
import { ModuleExports } from "@medusajs/types"
|
||||||
import { ProductModuleService } from "@services"
|
import { ProductModuleService } from "@services"
|
||||||
import loadContainer from "./loaders/container"
|
|
||||||
import loadConnection from "./loaders/connection"
|
import loadConnection from "./loaders/connection"
|
||||||
import * as ProductModels from "@models"
|
import loadContainer from "./loaders/container"
|
||||||
|
|
||||||
const service = ProductModuleService
|
const service = ProductModuleService
|
||||||
const loaders = [loadContainer, loadConnection] as any
|
const loaders = [loadContainer, loadConnection] as any
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types"
|
|
||||||
import { createConnection } from "../utils"
|
|
||||||
import * as ProductModels from "@models"
|
import * as ProductModels from "@models"
|
||||||
|
|
||||||
|
import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types"
|
||||||
|
|
||||||
import { EntitySchema } from "@mikro-orm/core"
|
import { EntitySchema } from "@mikro-orm/core"
|
||||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||||
|
import { createConnection } from "../utils"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This script is only valid for mikro orm managers. If a user provide a custom manager
|
* This script is only valid for mikro orm managers. If a user provide a custom manager
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
export { default as ProductModuleService } from "./product-module-service"
|
|
||||||
export { default as ProductService } from "./product"
|
export { default as ProductService } from "./product"
|
||||||
|
export { default as ProductCategoryService } from "./product-category"
|
||||||
|
export { default as ProductCollectionService } from "./product-collection"
|
||||||
|
export { default as ProductModuleService } from "./product-module-service"
|
||||||
export { default as ProductTagService } from "./product-tag"
|
export { default as ProductTagService } from "./product-tag"
|
||||||
export { default as ProductVariantService } from "./product-variant"
|
export { default as ProductVariantService } from "./product-variant"
|
||||||
export { default as ProductCollectionService } from "./product-collection"
|
|
||||||
export { default as ProductCategoryService } from "./product-category"
|
|
||||||
export { default as ProductTypeService } from "./product-type"
|
export { default as ProductTypeService } from "./product-type"
|
||||||
export { default as ProductOptionService } from "./product-option"
|
export { default as ProductOptionService } from "./product-option"
|
||||||
export { default as ProductImageService } from "./product-image"
|
export { default as ProductImageService } from "./product-image"
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { ProductCollection } from "@models"
|
|
||||||
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
||||||
import { ModulesSdkUtils, retrieveEntity } from "@medusajs/utils"
|
import { ModulesSdkUtils, retrieveEntity } from "@medusajs/utils"
|
||||||
|
|
||||||
|
import { ProductCollection } from "@models"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
productCollectionRepository: DAL.RepositoryService
|
productCollectionRepository: DAL.RepositoryService
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
DAL,
|
DAL,
|
||||||
FindConfig,
|
FindConfig,
|
||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
|
JoinerServiceConfig,
|
||||||
ProductTypes,
|
ProductTypes,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import ProductImageService from "./product-image"
|
import ProductImageService from "./product-image"
|
||||||
@@ -34,6 +35,7 @@ import {
|
|||||||
MedusaContext,
|
MedusaContext,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { shouldForceTransaction } from "../utils"
|
import { shouldForceTransaction } from "../utils"
|
||||||
|
import { joinerConfig } from "./../joiner-config"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
@@ -96,6 +98,10 @@ export default class ProductModuleService<
|
|||||||
this.productOptionService_ = productOptionService
|
this.productOptionService_ = productOptionService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__joinerConfig(): JoinerServiceConfig {
|
||||||
|
return joinerConfig
|
||||||
|
}
|
||||||
|
|
||||||
async list(
|
async list(
|
||||||
filters: ProductTypes.FilterableProductProps = {},
|
filters: ProductTypes.FilterableProductProps = {},
|
||||||
config: FindConfig<ProductTypes.ProductDTO> = {},
|
config: FindConfig<ProductTypes.ProductDTO> = {},
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import {
|
|||||||
Modules,
|
Modules,
|
||||||
} from "@medusajs/modules-sdk"
|
} from "@medusajs/modules-sdk"
|
||||||
import { IEventBusService, IStockLocationService } from "@medusajs/types"
|
import { IEventBusService, IStockLocationService } from "@medusajs/types"
|
||||||
import { StockLocationServiceInitializeOptions } from "../types"
|
|
||||||
import { moduleDefinition } from "../module-definition"
|
import { moduleDefinition } from "../module-definition"
|
||||||
|
import { StockLocationServiceInitializeOptions } from "../types"
|
||||||
|
|
||||||
export const initialize = async (
|
export const initialize = async (
|
||||||
options: StockLocationServiceInitializeOptions | ExternalModuleDeclaration,
|
options: StockLocationServiceInitializeOptions | ExternalModuleDeclaration,
|
||||||
@@ -15,7 +15,7 @@ export const initialize = async (
|
|||||||
}
|
}
|
||||||
): Promise<IStockLocationService> => {
|
): Promise<IStockLocationService> => {
|
||||||
const serviceKey = Modules.STOCK_LOCATION
|
const serviceKey = Modules.STOCK_LOCATION
|
||||||
const loaded = await MedusaModule.bootstrap(
|
const loaded = await MedusaModule.bootstrap<IStockLocationService>(
|
||||||
serviceKey,
|
serviceKey,
|
||||||
"@medusajs/stock-location",
|
"@medusajs/stock-location",
|
||||||
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||||
@@ -23,5 +23,5 @@ export const initialize = async (
|
|||||||
injectedDependencies
|
injectedDependencies
|
||||||
)
|
)
|
||||||
|
|
||||||
return loaded[serviceKey] as IStockLocationService
|
return loaded[serviceKey]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { JoinerServiceConfig } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const joinerConfig: JoinerServiceConfig = {
|
||||||
|
serviceName: Modules.STOCK_LOCATION,
|
||||||
|
primaryKeys: ["id"],
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "stock_location",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stock_locations",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
FilterableStockLocationProps,
|
FilterableStockLocationProps,
|
||||||
FindConfig,
|
FindConfig,
|
||||||
IEventBusService,
|
IEventBusService,
|
||||||
|
JoinerServiceConfig,
|
||||||
MODULE_RESOURCE_TYPE,
|
MODULE_RESOURCE_TYPE,
|
||||||
SharedContext,
|
SharedContext,
|
||||||
StockLocationAddressInput,
|
StockLocationAddressInput,
|
||||||
@@ -11,13 +12,13 @@ import {
|
|||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
InjectEntityManager,
|
InjectEntityManager,
|
||||||
isDefined,
|
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
|
isDefined,
|
||||||
setMetadata,
|
setMetadata,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import { joinerConfig } from "../joiner-config"
|
||||||
import { StockLocation, StockLocationAddress } from "../models"
|
import { StockLocation, StockLocationAddress } from "../models"
|
||||||
import { buildQuery } from "../utils/build-query"
|
import { buildQuery } from "../utils/build-query"
|
||||||
|
|
||||||
@@ -49,6 +50,10 @@ export default class StockLocationService {
|
|||||||
this.eventBusService_ = eventBusService
|
this.eventBusService_ = eventBusService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__joinerConfig(): JoinerServiceConfig {
|
||||||
|
return joinerConfig
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all stock locations that match the given selector.
|
* Lists all stock locations that match the given selector.
|
||||||
* @param selector - Properties to filter by.
|
* @param selector - Properties to filter by.
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from "./common"
|
export * from "./common"
|
||||||
export * from "./inventory"
|
export * from "./service"
|
||||||
@@ -13,9 +13,11 @@ import {
|
|||||||
} from "./common"
|
} from "./common"
|
||||||
|
|
||||||
import { FindConfig } from "../common"
|
import { FindConfig } from "../common"
|
||||||
|
import { JoinerServiceConfig } from "../joiner"
|
||||||
import { SharedContext } from ".."
|
import { SharedContext } from ".."
|
||||||
|
|
||||||
export interface IInventoryService {
|
export interface IInventoryService {
|
||||||
|
__joinerConfig(): JoinerServiceConfig
|
||||||
listInventoryItems(
|
listInventoryItems(
|
||||||
selector: FilterableInventoryItemProps,
|
selector: FilterableInventoryItemProps,
|
||||||
config?: FindConfig<InventoryItemDTO>,
|
config?: FindConfig<InventoryItemDTO>,
|
||||||
@@ -72,7 +74,7 @@ export interface IInventoryService {
|
|||||||
): Promise<InventoryItemDTO[]>
|
): Promise<InventoryItemDTO[]>
|
||||||
|
|
||||||
createInventoryLevel(
|
createInventoryLevel(
|
||||||
data: CreateInventoryLevelInput ,
|
data: CreateInventoryLevelInput,
|
||||||
context?: SharedContext
|
context?: SharedContext
|
||||||
): Promise<InventoryLevelDTO>
|
): Promise<InventoryLevelDTO>
|
||||||
|
|
||||||
@@ -4,16 +4,25 @@ export type JoinerRelationship = {
|
|||||||
primaryKey: string
|
primaryKey: string
|
||||||
serviceName: string
|
serviceName: string
|
||||||
inverse?: boolean // In an inverted relationship the foreign key is on the other service and the primary key is on the current service
|
inverse?: boolean // In an inverted relationship the foreign key is on the other service and the primary key is on the current service
|
||||||
|
isList?: boolean // Force the relationship to return a list
|
||||||
|
args?: Record<string, any> // Extra arguments to pass to the remoteFetchData callback
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JoinerServiceConfigAlias {
|
||||||
|
name: string
|
||||||
|
args?: Record<string, any> // Extra arguments to pass to the remoteFetchData callback
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JoinerServiceConfig {
|
export interface JoinerServiceConfig {
|
||||||
serviceName: string
|
serviceName: string
|
||||||
|
alias?: JoinerServiceConfigAlias | JoinerServiceConfigAlias[] // Property name to use as entrypoint to the service
|
||||||
primaryKeys: string[]
|
primaryKeys: string[]
|
||||||
relationships?: JoinerRelationship[]
|
relationships?: JoinerRelationship[]
|
||||||
extends?: {
|
extends?: {
|
||||||
serviceName: string
|
serviceName: string
|
||||||
resolve: JoinerRelationship
|
relationship: JoinerRelationship
|
||||||
}[]
|
}[]
|
||||||
|
args?: Record<string, any> // Extra arguments to pass to the remoteFetchData callback
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JoinerArgument {
|
export interface JoinerArgument {
|
||||||
@@ -23,7 +32,8 @@ export interface JoinerArgument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoteJoinerQuery {
|
export interface RemoteJoinerQuery {
|
||||||
service: string
|
service?: string
|
||||||
|
alias?: string
|
||||||
expands?: Array<{
|
expands?: Array<{
|
||||||
property: string
|
property: string
|
||||||
fields: string[]
|
fields: string[]
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ export type InternalModuleDeclaration = {
|
|||||||
dependencies?: string[]
|
dependencies?: string[]
|
||||||
resolve?: string
|
resolve?: string
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
|
alias?: string // If multiple modules are registered with the same key, the alias can be used to differentiate them
|
||||||
|
main?: boolean // If the module is the main module for the key when multiple ones are registered
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ExternalModuleDeclaration = {
|
export type ExternalModuleDeclaration = {
|
||||||
@@ -40,6 +42,8 @@ export type ExternalModuleDeclaration = {
|
|||||||
url: string
|
url: string
|
||||||
keepAlive: boolean
|
keepAlive: boolean
|
||||||
}
|
}
|
||||||
|
alias?: string // If multiple modules are registered with the same key, the alias can be used to differentiate them
|
||||||
|
main?: boolean // If the module is the main module for the key when multiple ones are registered
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModuleResolution = {
|
export type ModuleResolution = {
|
||||||
@@ -58,6 +62,7 @@ export type ModuleDefinition = {
|
|||||||
label: string
|
label: string
|
||||||
canOverride?: boolean
|
canOverride?: boolean
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isQueryable?: boolean // If the modules should be queryable via Remote Joiner
|
||||||
dependencies?: string[]
|
dependencies?: string[]
|
||||||
defaultModuleDeclaration:
|
defaultModuleDeclaration:
|
||||||
| InternalModuleDeclaration
|
| InternalModuleDeclaration
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from "./service"
|
|
||||||
export * from "./common"
|
export * from "./common"
|
||||||
|
export * from "./service"
|
||||||
@@ -11,10 +11,14 @@ import {
|
|||||||
ProductTagDTO,
|
ProductTagDTO,
|
||||||
ProductVariantDTO,
|
ProductVariantDTO,
|
||||||
} from "./common"
|
} from "./common"
|
||||||
import { FindConfig } from "../common"
|
|
||||||
import { Context } from "../shared-context"
|
import { Context } from "../shared-context"
|
||||||
|
import { FindConfig } from "../common"
|
||||||
|
import { JoinerServiceConfig } from "../joiner"
|
||||||
|
|
||||||
export interface IProductModuleService {
|
export interface IProductModuleService {
|
||||||
|
__joinerConfig(): JoinerServiceConfig
|
||||||
|
|
||||||
retrieve(productId: string, sharedContext?: Context): Promise<ProductDTO>
|
retrieve(productId: string, sharedContext?: Context): Promise<ProductDTO>
|
||||||
|
|
||||||
list(
|
list(
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from "./common"
|
export * from "./common"
|
||||||
export * from "./stock-location"
|
export * from "./service"
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { FindConfig } from "../common/common"
|
||||||
|
import { JoinerServiceConfig } from "../joiner"
|
||||||
|
import { SharedContext } from "../shared-context"
|
||||||
|
import {
|
||||||
|
CreateStockLocationInput,
|
||||||
|
FilterableStockLocationProps,
|
||||||
|
StockLocationDTO,
|
||||||
|
UpdateStockLocationInput,
|
||||||
|
} from "./common"
|
||||||
|
|
||||||
|
export interface IStockLocationService {
|
||||||
|
__joinerConfig(): JoinerServiceConfig
|
||||||
|
list(
|
||||||
|
selector: FilterableStockLocationProps,
|
||||||
|
config?: FindConfig<StockLocationDTO>,
|
||||||
|
context?: SharedContext
|
||||||
|
): Promise<StockLocationDTO[]>
|
||||||
|
|
||||||
|
listAndCount(
|
||||||
|
selector: FilterableStockLocationProps,
|
||||||
|
config?: FindConfig<StockLocationDTO>,
|
||||||
|
context?: SharedContext
|
||||||
|
): Promise<[StockLocationDTO[], number]>
|
||||||
|
|
||||||
|
retrieve(
|
||||||
|
id: string,
|
||||||
|
config?: FindConfig<StockLocationDTO>,
|
||||||
|
context?: SharedContext
|
||||||
|
): Promise<StockLocationDTO>
|
||||||
|
|
||||||
|
create(
|
||||||
|
input: CreateStockLocationInput,
|
||||||
|
context?: SharedContext
|
||||||
|
): Promise<StockLocationDTO>
|
||||||
|
|
||||||
|
update(
|
||||||
|
id: string,
|
||||||
|
input: UpdateStockLocationInput,
|
||||||
|
context?: SharedContext
|
||||||
|
): Promise<StockLocationDTO>
|
||||||
|
|
||||||
|
delete(id: string, context?: SharedContext): Promise<void>
|
||||||
|
}
|
||||||
@@ -6403,7 +6403,6 @@ __metadata:
|
|||||||
uuid: ^9.0.0
|
uuid: ^9.0.0
|
||||||
winston: ^3.8.2
|
winston: ^3.8.2
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@medusajs/types": ^1.8.7
|
|
||||||
medusa-interfaces: ^1.3.7
|
medusa-interfaces: ^1.3.7
|
||||||
typeorm: ^0.3.16
|
typeorm: ^0.3.16
|
||||||
bin:
|
bin:
|
||||||
@@ -6415,7 +6414,8 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@medusajs/modules-sdk@workspace:packages/modules-sdk"
|
resolution: "@medusajs/modules-sdk@workspace:packages/modules-sdk"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@medusajs/types": ^1.8.8
|
"@medusajs/orchestration": ^0.0.2
|
||||||
|
"@medusajs/types": ^1.8.11
|
||||||
"@medusajs/utils": ^1.9.1
|
"@medusajs/utils": ^1.9.1
|
||||||
awilix: ^8.0.0
|
awilix: ^8.0.0
|
||||||
cross-env: ^5.2.1
|
cross-env: ^5.2.1
|
||||||
@@ -6472,11 +6472,11 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@medusajs/orchestration@workspace:packages/orchestration":
|
"@medusajs/orchestration@^0.0.2, @medusajs/orchestration@workspace:packages/orchestration":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@medusajs/orchestration@workspace:packages/orchestration"
|
resolution: "@medusajs/orchestration@workspace:packages/orchestration"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@medusajs/types": ^1.8.10
|
"@medusajs/types": ^1.8.11
|
||||||
"@medusajs/utils": ^1.9.2
|
"@medusajs/utils": ^1.9.2
|
||||||
cross-env: ^5.2.1
|
cross-env: ^5.2.1
|
||||||
graphql: ^16.6.0
|
graphql: ^16.6.0
|
||||||
|
|||||||
Reference in new issue
Block a user