feat(medusa, stock-location, inventory): Allow modules to integrate with core (#2997)

* feat: module shared resources
This commit is contained in:
Carlos R. L. Rodrigues authored and GitHub committed 2023-01-13 10:39:43 -03:00
1 parent b3e4be7208
commit 9dbccd9ca7
24 files changed
+577 -327

No files matched your search

-7
View File
@@ -1,7 +0,0 @@
import ConnectionLoader from "./loaders/connection"
import StockLocationService from "./services/stock-location"
import * as SchemaMigration from "./migrations/schema-migrations/1665749860179-setup"
export const service = StockLocationService
export const migrations = [SchemaMigration]
export const loaders = [ConnectionLoader]
+20
View File
@@ -0,0 +1,20 @@
import ConnectionLoader from "./loaders/connection"
import StockLocationService from "./services/stock-location"
import * as SchemaMigration from "./migrations/schema-migrations/1665749860179-setup"
import * as StockLocationModels from "./models"
import { ModuleExports } from "@medusajs/medusa"
const service = StockLocationService
const migrations = [SchemaMigration]
const loaders = [ConnectionLoader]
const models = Object.values(StockLocationModels)
const moduleDefinition: ModuleExports = {
service,
migrations,
loaders,
models,
}
export default moduleDefinition
@@ -1,22 +1,6 @@
import { ConfigModule } from "@medusajs/medusa"
import { ConnectionOptions, createConnection } from "typeorm"
import { CONNECTION_NAME } from "../config"
import { ConfigurableModuleDeclaration, LoaderOptions } from "@medusajs/medusa"
import { StockLocation, StockLocationAddress } from "../models"
export default async ({
configModule,
}: {
configModule: ConfigModule
}): Promise<void> => {
await createConnection({
name: CONNECTION_NAME,
type: configModule.projectConfig.database_type,
url: configModule.projectConfig.database_url,
database: configModule.projectConfig.database_database,
schema: configModule.projectConfig.database_schema,
extra: configModule.projectConfig.database_extra || {},
entities: [StockLocation, StockLocationAddress],
logging: configModule.projectConfig.database_logging || false,
} as ConnectionOptions)
}
export default async (
{ configModule }: LoaderOptions,
moduleDeclaration?: ConfigurableModuleDeclaration
): Promise<void> => {}
@@ -1,4 +1,4 @@
import { getConnection, EntityManager } from "typeorm"
import { EntityManager } from "typeorm"
import { isDefined, MedusaError } from "medusa-core-utils"
import {
FindConfig,
@@ -9,12 +9,15 @@ import {
StockLocationAddressInput,
IEventBusService,
setMetadata,
TransactionBaseService,
ConfigurableModuleDeclaration,
MODULE_RESOURCE_TYPE,
} from "@medusajs/medusa"
import { StockLocation, StockLocationAddress } from "../models"
import { CONNECTION_NAME } from "../config"
type InjectedDependencies = {
manager: EntityManager
eventBusService: IEventBusService
}
@@ -22,24 +25,38 @@ type InjectedDependencies = {
* Service for managing stock locations.
*/
export default class StockLocationService {
export default class StockLocationService extends TransactionBaseService {
static Events = {
CREATED: "stock-location.created",
UPDATED: "stock-location.updated",
DELETED: "stock-location.deleted",
}
protected readonly manager_: EntityManager
protected manager_: EntityManager
protected transactionManager_: EntityManager | undefined
protected readonly eventBusService_: IEventBusService
constructor({ eventBusService }: InjectedDependencies) {
constructor(
{ eventBusService, manager }: InjectedDependencies,
options?: unknown,
moduleDeclaration?: ConfigurableModuleDeclaration
) {
super(arguments[0])
if (moduleDeclaration?.resources !== MODULE_RESOURCE_TYPE.SHARED) {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
"At the moment this module can only be used with shared resources"
)
}
this.eventBusService_ = eventBusService
this.manager_ = manager
}
private getManager(): EntityManager {
const connection = getConnection(CONNECTION_NAME)
return connection.manager
return this.transactionManager_ ?? this.manager_
}
/**
@@ -99,7 +116,7 @@ export default class StockLocationService {
const locationRepo = manager.getRepository(StockLocation)
const query = buildQuery({ id: stockLocationId }, config)
const loc = await locationRepo.findOne(query)
const [loc] = await locationRepo.find(query)
if (!loc) {
throw new MedusaError(
@@ -117,8 +134,7 @@ export default class StockLocationService {
* @returns {Promise<StockLocation>} - The created stock location.
*/
async create(data: CreateStockLocationInput): Promise<StockLocation> {
const defaultManager = this.getManager()
return await defaultManager.transaction(async (manager) => {
return await this.atomicPhase_(async (manager) => {
const locationRepo = manager.getRepository(StockLocation)
const loc = locationRepo.create({
@@ -147,9 +163,11 @@ export default class StockLocationService {
const result = await locationRepo.save(loc)
await this.eventBusService_.emit(StockLocationService.Events.CREATED, {
id: result.id,
})
await this.eventBusService_
.withTransaction(manager)
.emit(StockLocationService.Events.CREATED, {
id: result.id,
})
return result
})
@@ -166,17 +184,16 @@ export default class StockLocationService {
stockLocationId: string,
updateData: UpdateStockLocationInput
): Promise<StockLocation> {
const defaultManager = this.getManager()
return await defaultManager.transaction(async (manager) => {
return await this.atomicPhase_(async (manager) => {
const locationRepo = manager.getRepository(StockLocation)
const item = await this.retrieve(stockLocationId)
const { address, metadata, ...data } = updateData
const { address, ...data } = updateData
if (address) {
if (item.address_id) {
await this.updateAddress(item.address_id, address, { manager })
await this.updateAddress(item.address_id, address)
} else {
const locAddressRepo = manager.getRepository(StockLocationAddress)
const locAddress = locAddressRepo.create(address)
@@ -185,16 +202,20 @@ export default class StockLocationService {
}
}
const { metadata, ...fields } = updateData
const toSave = locationRepo.merge(item, fields)
if (metadata) {
item.metadata = setMetadata(item, metadata)
toSave.metadata = setMetadata(toSave, metadata)
}
const toSave = locationRepo.merge(item, data)
await locationRepo.save(toSave)
await this.eventBusService_.emit(StockLocationService.Events.UPDATED, {
id: stockLocationId,
})
await this.eventBusService_
.withTransaction(manager)
.emit(StockLocationService.Events.UPDATED, {
id: stockLocationId,
})
return item
})
@@ -204,35 +225,42 @@ export default class StockLocationService {
* Updates an address for a stock location.
* @param {string} addressId - The ID of the address to update.
* @param {StockLocationAddressInput} address - The update data for the address.
* @param {Object} context - Context for the update.
* @param {EntityManager} context.manager - The entity manager to use for the update.
* @returns {Promise<StockLocationAddress>} - The updated stock location address.
*/
protected async updateAddress(
addressId: string,
address: StockLocationAddressInput,
context: { manager?: EntityManager } = {}
address: StockLocationAddressInput
): Promise<StockLocationAddress> {
const manager = context.manager || this.getManager()
const locationAddressRepo = manager.getRepository(StockLocationAddress)
const existingAddress = await locationAddressRepo.findOne(addressId)
if (!existingAddress) {
if (!isDefined(addressId)) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`StockLocation address with id ${addressId} was not found`
`"addressId" must be defined`
)
}
const toSave = locationAddressRepo.merge(existingAddress, address)
return await this.atomicPhase_(async (manager) => {
const locationAddressRepo = manager.getRepository(StockLocationAddress)
const { metadata } = address
if (metadata) {
toSave.metadata = setMetadata(toSave, metadata)
}
const existingAddress = await locationAddressRepo.findOne({
id: addressId,
})
if (!existingAddress) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`StockLocation address with id ${addressId} was not found`
)
}
return await locationAddressRepo.save(toSave)
const { metadata, ...fields } = address
const toSave = locationAddressRepo.merge(existingAddress, fields)
if (metadata) {
toSave.metadata = setMetadata(toSave, metadata)
}
return await locationAddressRepo.save(toSave)
})
}
/**
@@ -241,13 +269,16 @@ export default class StockLocationService {
* @returns {Promise<void>} - An empty promise.
*/
async delete(id: string): Promise<void> {
const manager = this.getManager()
const locationRepo = manager.getRepository(StockLocation)
return await this.atomicPhase_(async (manager) => {
const locationRepo = manager.getRepository(StockLocation)
await locationRepo.softRemove({ id })
await locationRepo.softRemove({ id })
await this.eventBusService_.emit(StockLocationService.Events.DELETED, {
id,
await this.eventBusService_
.withTransaction(manager)
.emit(StockLocationService.Events.DELETED, {
id,
})
})
}
}