feat(medusa-js, medusa-react, medusa): Prepare API for admin implementations (#3110)

********What********
Add `joinSalesChannels util to stock locations

Add the following endpoints to medusa-react
- inventory items
    - mutations
        - update
        - delete
        - update location level
        - delete location level
        - create location level
    - queries
        - list inventory items
        - get inventory item
        - list location levels
- Stock locations
    - mutations
        - create stock location
        - update stock location
        - delete stock location
    - queries
        - list stock locations
        - get stock locatoin
- Variants
    - queries
        - get inventory
- Reservations
    - mutations
        - create reservation
        - update reservation
        - delete reservation
    - queries
        - list reservations
        - get reservation
- sales channels 
  - mutations
    - associate location with sc
    - remove location association

**Why**
- Update clients to reflect new api endpoints in the core with inventory modules

Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
Rares StefanandPhilip Korsholm authored and GitHub committed 2023-02-16 08:49:48 +00:00
1 parent 121b42acfe
commit 12d304307a
48 files changed
+1756 -57

No files matched your search

+1
View File
@@ -43,6 +43,7 @@ export * from "./routes/admin/publishable-api-keys"
export * from "./routes/admin/regions"
export * from "./routes/admin/return-reasons"
export * from "./routes/admin/returns"
export * from "./routes/admin/reservations"
export * from "./routes/admin/sales-channels"
export * from "./routes/admin/shipping-options"
export * from "./routes/admin/shipping-profiles"
@@ -82,7 +82,6 @@ export default async (req, res) => {
* @schema AdminPostReservationsReq
* type: object
* required:
* - line_item_id
* - location_id
* - inventory_item_id
* - quantity
@@ -109,3 +109,4 @@ export * from "./create-reservation"
export * from "./delete-reservation"
export * from "./get-reservation"
export * from "./update-reservation"
export * from "./list-reservations"
@@ -15,6 +15,7 @@ import { AdminGetSalesChannelsParams } from "./list-sales-channels"
import { AdminPostSalesChannelsSalesChannelReq } from "./update-sales-channel"
import { AdminPostSalesChannelsChannelStockLocationsReq } from "./associate-stock-location"
import { AdminDeleteSalesChannelsChannelStockLocationsReq } from "./remove-stock-location"
import { checkRegisteredModules } from "../../../middlewares/check-registered-modules"
const route = Router()
@@ -47,11 +48,19 @@ export default (app) => {
)
salesChannelRouter.post(
"/stock-locations",
checkRegisteredModules({
stockLocationService:
"Stock Locations are not enabled. Please add a Stock Location module to enable this functionality.",
}),
transformBody(AdminPostSalesChannelsChannelStockLocationsReq),
middlewares.wrap(require("./associate-stock-location").default)
)
salesChannelRouter.delete(
"/stock-locations",
checkRegisteredModules({
stockLocationService:
"Stock Locations are not enabled. Please add a Stock Location module to enable this functionality.",
}),
transformBody(AdminDeleteSalesChannelsChannelStockLocationsReq),
middlewares.wrap(require("./remove-stock-location").default)
)
@@ -1,6 +1,11 @@
import { IStockLocationService } from "../../../../interfaces"
import { Request, Response } from "express"
import { FindParams } from "../../../../types/common"
import { joinSalesChannels } from "./utils/join-sales-channels"
import {
SalesChannelLocationService,
SalesChannelService,
} from "../../../../services"
/**
* @oas [get] /stock-locations/{id}
@@ -50,7 +55,34 @@ export default async (req: Request, res: Response) => {
const locationService: IStockLocationService = req.scope.resolve(
"stockLocationService"
)
const stockLocation = await locationService.retrieve(id, req.retrieveConfig)
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
"salesChannelLocationService"
)
const salesChannelService: SalesChannelService = req.scope.resolve(
"salesChannelService"
)
const { retrieveConfig } = req
const includeSalesChannels =
!!retrieveConfig.relations?.includes("sales_channels")
if (includeSalesChannels) {
retrieveConfig.relations = retrieveConfig.relations?.filter(
(r) => r !== "sales_channels"
)
}
let stockLocation = await locationService.retrieve(id, retrieveConfig)
if (includeSalesChannels) {
const [location] = await joinSalesChannels(
[stockLocation],
channelLocationService,
salesChannelService
)
stockLocation = location
}
res.status(200).json({ stock_location: stockLocation })
}
@@ -1,7 +1,10 @@
import { Router } from "express"
import "reflect-metadata"
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
import { StockLocationDTO } from "../../../../types/stock-location"
import {
StockLocationDTO,
StockLocationExpandedDTO,
} from "../../../../types/stock-location"
import middlewares, {
transformBody,
transformQuery,
@@ -113,10 +116,10 @@ export type AdminStockLocationsDeleteRes = DeleteResponse
* type: object
* properties:
* stock_location:
* $ref: "#/components/schemas/StockLocationDTO"
* $ref: "#/components/schemas/StockLocationExpandedDTO"
*/
export type AdminStockLocationsRes = {
stock_location: StockLocationDTO
stock_location: StockLocationExpandedDTO
}
/**
@@ -126,7 +129,7 @@ export type AdminStockLocationsRes = {
* stock_locations:
* type: array
* items:
* $ref: "#/components/schemas/StockLocationDTO"
* $ref: "#/components/schemas/StockLocationExpandedDTO"
* count:
* type: integer
* description: The total number of items available
@@ -138,7 +141,7 @@ export type AdminStockLocationsRes = {
* description: The number of items per page
*/
export type AdminStockLocationsListRes = PaginatedResponse & {
stock_locations: StockLocationDTO[]
stock_locations: StockLocationExpandedDTO[]
}
export * from "./list-stock-locations"
@@ -4,6 +4,11 @@ import { IsType } from "../../../../utils/validators/is-type"
import { IStockLocationService } from "../../../../interfaces"
import { extendedFindParamsMixin } from "../../../../types/common"
import { Request, Response } from "express"
import {
SalesChannelLocationService,
SalesChannelService,
} from "../../../../services"
import { joinSalesChannels } from "./utils/join-sales-channels"
/**
* @oas [get] /stock-locations
@@ -133,15 +138,38 @@ export default async (req: Request, res: Response) => {
const stockLocationService: IStockLocationService = req.scope.resolve(
"stockLocationService"
)
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
"salesChannelLocationService"
)
const salesChannelService: SalesChannelService = req.scope.resolve(
"salesChannelService"
)
const { filterableFields, listConfig } = req
const { skip, take } = listConfig
const [locations, count] = await stockLocationService.listAndCount(
const includeSalesChannels =
!!listConfig.relations?.includes("sales_channels")
if (includeSalesChannels) {
listConfig.relations = listConfig.relations?.filter(
(r) => r !== "sales_channels"
)
}
let [locations, count] = await stockLocationService.listAndCount(
filterableFields,
listConfig
)
if (includeSalesChannels) {
locations = await joinSalesChannels(
locations,
channelLocationService,
salesChannelService
)
}
res.status(200).json({
stock_locations: locations,
count,
@@ -0,0 +1,31 @@
import {
SalesChannelLocationService,
SalesChannelService,
} from "../../../../../services"
import {
StockLocationDTO,
StockLocationExpandedDTO,
} from "../../../../../types/stock-location"
const joinSalesChannels = async (
locations: StockLocationDTO[],
channelLocationService: SalesChannelLocationService,
salesChannelService: SalesChannelService
): Promise<StockLocationExpandedDTO[]> => {
return await Promise.all(
locations.map(async (location: StockLocationExpandedDTO) => {
const salesChannelIds = await channelLocationService.listSalesChannelIds(
location.id
)
const [salesChannels] = await salesChannelService.listAndCount({
id: salesChannelIds,
})
location.sales_channels = salesChannels
return location
})
)
}
export { joinSalesChannels }
@@ -1,11 +1,9 @@
import { FulfillmentProvider, PaymentProvider, Store } from "../../../../models"
import {
FulfillmentProviderService,
PaymentProviderService,
StoreService,
} from "../../../../services"
import { FeatureFlagsResponse } from "../../../../types/feature-flags"
import { ModulesResponse } from "../../../../types/modules"
import { ExtendedStoreDTO } from "../../../../types/store"
import { FlagRouter } from "../../../../utils/flag-router"
import { ModulesHelper } from "../../../../utils/module-helper"
@@ -77,12 +75,7 @@ export default async (req, res) => {
const data = (await storeService.retrieve({
relations,
})) as Store & {
payment_providers: PaymentProvider[]
fulfillment_providers: FulfillmentProvider[]
feature_flags: FeatureFlagsResponse
modules: ModulesResponse
}
})) as ExtendedStoreDTO
data.feature_flags = featureFlagRouter.listFlags()
data.modules = modulesHelper.modules
@@ -1,6 +1,7 @@
import { Router } from "express"
import { PaymentProvider, Store, TaxProvider } from "./../../../../"
import { PaymentProvider, TaxProvider } from "./../../../../"
import middlewares from "../../../middlewares"
import { ExtendedStoreDTO } from "../../../../types/store"
const route = Router()
@@ -34,10 +35,10 @@ export default (app) => {
* type: object
* properties:
* store:
* $ref: "#/components/schemas/Store"
* $ref: "#/components/schemas/ExtendedStoreDTO"
*/
export type AdminStoresRes = {
store: Store
store: ExtendedStoreDTO
}
/**
@@ -72,7 +72,9 @@ export default async (req, res) => {
const inventoryService: IInventoryService =
req.scope.resolve("inventoryService")
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
"salesChannelLocationService"
)
const channelService: SalesChannelService = req.scope.resolve(
"salesChannelService"
)
@@ -91,11 +93,17 @@ export default async (req, res) => {
sales_channel_availability: [],
}
const [channels] = await channelService.listAndCount(
{},
{
relations: ["locations"],
}
const [rawChannels] = await channelService.listAndCount({})
const channels: SalesChannelDTO[] = await Promise.all(
rawChannels.map(async (channel) => {
const locationIds = await channelLocationService.listLocationIds(
channel.id
)
return {
...channel,
locations: locationIds,
}
})
)
const inventory =
@@ -116,7 +124,7 @@ export default async (req, res) => {
const quantity = await inventoryService.retrieveAvailableQuantity(
inventory[0].id,
channel.locations.map((loc) => loc.id)
channel.locations
)
return {
@@ -133,6 +141,10 @@ export default async (req, res) => {
})
}
type SalesChannelDTO = Omit<SalesChannel, "beforeInsert" | "locations"> & {
locations: string[]
}
type ResponseInventoryItem = Partial<InventoryItemDTO> & {
location_levels?: InventoryLevelDTO[]
}