Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 21 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
98cbc1b
chore: module shared resources
carlos-r-l-rodrigues Jan 11, 2023
9f23db1
Merge branch 'develop' into chore/module-share-resources
carlos-r-l-rodrigues Jan 11, 2023
fb2b2d1
Limit module to use only as shared
carlos-r-l-rodrigues Jan 11, 2023
b4ff7f8
Merge branch 'chore/module-share-resources' of github.com:medusajs/me…
carlos-r-l-rodrigues Jan 11, 2023
d36ac4c
Merge branch 'develop' into chore/module-share-resources
carlos-r-l-rodrigues Jan 11, 2023
3b5a5e6
addressing comments
carlos-r-l-rodrigues Jan 12, 2023
f9c15e0
Merge branch 'chore/module-share-resources' of github.com:medusajs/me…
carlos-r-l-rodrigues Jan 12, 2023
2f73bdf
Merge branch 'develop' into chore/module-share-resources
carlos-r-l-rodrigues Jan 12, 2023
d94330e
fix: findOne param
carlos-r-l-rodrigues Jan 13, 2023
4b0a373
Merge branch 'chore/module-share-resources' of github.com:medusajs/me…
carlos-r-l-rodrigues Jan 13, 2023
f7ae4c3
Merge branch 'develop' into chore/module-share-resources
carlos-r-l-rodrigues Jan 13, 2023
1b3acf5
remove context
carlos-r-l-rodrigues Jan 13, 2023
2f5fcf7
Merge branch 'chore/module-share-resources' of github.com:medusajs/me…
carlos-r-l-rodrigues Jan 13, 2023
9731db4
oas
carlos-r-l-rodrigues Jan 13, 2023
1e7c5da
Create afraid-moles-brake.md
olivermrbl Jan 13, 2023
ba7b065
utils
carlos-r-l-rodrigues Jan 13, 2023
d73ec61
Merge branch 'chore/module-share-resources' of github.com:medusajs/me…
carlos-r-l-rodrigues Jan 13, 2023
e36af54
Merge branch 'develop' into chore/module-share-resources
carlos-r-l-rodrigues Jan 13, 2023
8e5a317
metadata merge
carlos-r-l-rodrigues Jan 13, 2023
a8bf8b0
Merge branch 'chore/module-share-resources' of github.com:medusajs/me…
carlos-r-l-rodrigues Jan 13, 2023
1e12b8b
metadata
carlos-r-l-rodrigues Jan 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/afraid-moles-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@medusajs/inventory": patch
"@medusajs/medusa": patch
"@medusajs/stock-location": patch
---

feat(medusa, stock-location, inventory): Allow modules to integrate with core
7 changes: 0 additions & 7 deletions packages/inventory/src/index.js

This file was deleted.

19 changes: 19 additions & 0 deletions packages/inventory/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import ConnectionLoader from "./loaders/connection"
import InventoryService from "./services/inventory"
import * as InventoryModels from "./models"
import * as SchemaMigration from "./migrations/schema-migrations/1665748086258-inventory_setup"
import { ModuleExports } from "@medusajs/medusa"

const service = InventoryService
const migrations = [SchemaMigration]
const loaders = [ConnectionLoader]
const models = Object.values(InventoryModels)

const moduleDefinition: ModuleExports = {
carlos-r-l-rodrigues marked this conversation as resolved.
Show resolved Hide resolved
service,
migrations,
loaders,
models,
}

export default moduleDefinition
26 changes: 5 additions & 21 deletions packages/inventory/src/loaders/connection.ts
Original file line number Diff line number Diff line change
@@ -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 { ReservationItem, InventoryItem, InventoryLevel } 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: [ReservationItem, InventoryLevel, InventoryItem],
logging: configModule.projectConfig.database_logging || false,
} as ConnectionOptions)
}
export default async (
{ configModule }: LoaderOptions,
moduleDeclaration?: ConfigurableModuleDeclaration
): Promise<void> => {}
176 changes: 65 additions & 111 deletions packages/inventory/src/services/inventory-item.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
import { ILike, In, getConnection, DeepPartial, EntityManager } from "typeorm"
import { DeepPartial, EntityManager } from "typeorm"
import { isDefined, MedusaError } from "medusa-core-utils"
import {
FindConfig,
buildQuery,
IEventBusService,
FilterableInventoryItemProps,
CreateInventoryItemInput,
InventoryItemDTO,
TransactionBaseService,
} from "@medusajs/medusa"

import { InventoryItem } from "../models"
import { CONNECTION_NAME } from "../config"
import { getListQuery } from "../utils/query"

type InjectedDependencies = {
eventBusService: IEventBusService
manager: EntityManager
}

export default class InventoryItemService {
export default class InventoryItemService extends TransactionBaseService {
static Events = {
CREATED: "inventory-item.created",
UPDATED: "inventory-item.updated",
DELETED: "inventory-item.deleted",
}

protected readonly eventBusService_: IEventBusService
protected manager_: EntityManager
protected transactionManager_: EntityManager | undefined

constructor({ eventBusService, manager }: InjectedDependencies) {
super(arguments[0])

constructor({ eventBusService }: InjectedDependencies) {
this.eventBusService_ = eventBusService
this.manager_ = manager
}

private getManager(): EntityManager {
const connection = getConnection(CONNECTION_NAME)
return connection.manager
return this.transactionManager_ ?? this.manager_
}

/**
Expand All @@ -41,73 +48,11 @@ export default class InventoryItemService {
async list(
selector: FilterableInventoryItemProps = {},
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 }
): Promise<InventoryItem[]> {
const queryBuilder = this.getListQuery(selector, config)
): Promise<InventoryItemDTO[]> {
const queryBuilder = getListQuery(this.getManager(), selector, config)
return await queryBuilder.getMany()
}

private getListQuery(
selector: FilterableInventoryItemProps = {},
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 }
) {
const manager = this.getManager()
const inventoryItemRepository = manager.getRepository(InventoryItem)
const query = buildQuery(selector, config)

const queryBuilder = inventoryItemRepository.createQueryBuilder("inv_item")

if (query.where.q) {
query.where.sku = ILike(`%${query.where.q as string}%`)

delete query.where.q
}

if ("location_id" in query.where) {
const locationIds = Array.isArray(selector.location_id)
? selector.location_id
: [selector.location_id]

queryBuilder.innerJoin(
"inventory_level",
"level",
"level.inventory_item_id = inv_item.id AND level.location_id IN (:...locationIds)",
{ locationIds }
)

delete query.where.location_id
}

if (query.take) {
queryBuilder.take(query.take)
}

if (query.skip) {
queryBuilder.skip(query.skip)
}

if (query.where) {
queryBuilder.where(query.where)
}

if (query.select) {
queryBuilder.select(query.select.map((s) => "inv_item." + s))
}

if (query.order) {
const toSelect: string[] = []
const parsed = Object.entries(query.order).reduce((acc, [k, v]) => {
const key = `inv_item.${k}`
toSelect.push(key)
acc[key] = v
return acc
}, {})
queryBuilder.addSelect(toSelect)
queryBuilder.orderBy(parsed)
}

return queryBuilder
}

/**
* @param selector - Filter options for inventory items.
* @param config - Configuration for query.
Expand All @@ -116,8 +61,8 @@ export default class InventoryItemService {
async listAndCount(
selector: FilterableInventoryItemProps = {},
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 }
): Promise<[InventoryItem[], number]> {
const queryBuilder = this.getListQuery(selector, config)
): Promise<[InventoryItemDTO[], number]> {
const queryBuilder = getListQuery(this.getManager(), selector, config)
return await queryBuilder.getManyAndCount()
}

Expand Down Expand Up @@ -160,30 +105,33 @@ export default class InventoryItemService {
* @return The newly created inventory item.
*/
async create(data: CreateInventoryItemInput): Promise<InventoryItem> {
const manager = this.getManager()
const itemRepository = manager.getRepository(InventoryItem)
return await this.atomicPhase_(async (manager) => {
const itemRepository = manager.getRepository(InventoryItem)

const inventoryItem = itemRepository.create({
sku: data.sku,
origin_country: data.origin_country,
metadata: data.metadata,
hs_code: data.hs_code,
mid_code: data.mid_code,
material: data.material,
weight: data.weight,
length: data.length,
height: data.height,
width: data.width,
requires_shipping: data.requires_shipping,
})

const inventoryItem = itemRepository.create({
sku: data.sku,
origin_country: data.origin_country,
metadata: data.metadata,
hs_code: data.hs_code,
mid_code: data.mid_code,
material: data.material,
weight: data.weight,
length: data.length,
height: data.height,
width: data.width,
requires_shipping: data.requires_shipping,
})
const result = await itemRepository.save(inventoryItem)

const result = await itemRepository.save(inventoryItem)
await this.eventBusService_
.withTransaction(manager)
.emit(InventoryItemService.Events.CREATED, {
id: result.id,
})

await this.eventBusService_.emit(InventoryItemService.Events.CREATED, {
id: result.id,
return result
})

return result
}

/**
Expand All @@ -198,38 +146,44 @@ export default class InventoryItemService {
"id" | "created_at" | "metadata" | "deleted_at"
>
): Promise<InventoryItem> {
const manager = this.getManager()
const itemRepository = manager.getRepository(InventoryItem)
return await this.atomicPhase_(async (manager) => {
const itemRepository = manager.getRepository(InventoryItem)

const item = await this.retrieve(inventoryItemId)
const item = await this.retrieve(inventoryItemId)

const shouldUpdate = Object.keys(data).some((key) => {
return item[key] !== data[key]
})
const shouldUpdate = Object.keys(data).some((key) => {
return item[key] !== data[key]
})

if (shouldUpdate) {
itemRepository.merge(item, data)
await itemRepository.save(item)
if (shouldUpdate) {
itemRepository.merge(item, data)
await itemRepository.save(item)

await this.eventBusService_.emit(InventoryItemService.Events.UPDATED, {
id: item.id,
})
}
await this.eventBusService_
.withTransaction(manager)
carlos-r-l-rodrigues marked this conversation as resolved.
Show resolved Hide resolved
.emit(InventoryItemService.Events.UPDATED, {
id: item.id,
})
}

return item
return item
})
}

/**
* @param inventoryItemId - The id of the inventory item to delete.
*/
async delete(inventoryItemId: string): Promise<void> {
const manager = this.getManager()
const itemRepository = manager.getRepository(InventoryItem)
await this.atomicPhase_(async (manager) => {
const itemRepository = manager.getRepository(InventoryItem)

await itemRepository.softRemove({ id: inventoryItemId })
await itemRepository.softRemove({ id: inventoryItemId })

await this.eventBusService_.emit(InventoryItemService.Events.DELETED, {
id: inventoryItemId,
await this.eventBusService_
.withTransaction(manager)
.emit(InventoryItemService.Events.DELETED, {
id: inventoryItemId,
})
})
}
}
Loading