Skip to content

Commit

Permalink
feat(medusa, stock-location-next): add list-stock-locations endpoint …
Browse files Browse the repository at this point in the history
…to api-v2 (#6788)

* initial create

* add list for stock locations

* add changeset

* redo changes for stock locatino module'

* add changeset

* naming

* prep for pr

* move integration tests

* fix pr feedback

* add changeset

* update changeset

---------

Co-authored-by: Riqwan Thamir <[email protected]>
  • Loading branch information
pKorsholm and riqwan authored Mar 28, 2024
1 parent a6562d2 commit 18438a6
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .changeset/wild-houses-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@medusajs/stock-location-next": patch
"@medusajs/medusa": patch
---

feat(medusa, stock-location-next): add list-stock-locations endpoint to api-v2
95 changes: 95 additions & 0 deletions integration-tests/api/__tests__/admin/stock-location/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,101 @@ medusaIntegrationTestRunner({
})
})

describe("list stock locations", () => {
let location1
let location2
beforeEach(async () => {
const location1CreateResponse = await api.post(
`/admin/stock-locations`,
{
name: "Test Location 1",
address: {
address_1: "Test Address",
country_code: "US",
},
},
adminHeaders
)
location1 = location1CreateResponse.data.stock_location
const location2CreateResponse = await api.post(
`/admin/stock-locations`,
{
name: "Test Location 2",
address: {
address_1: "Test Address",
country_code: "US",
},
},
adminHeaders
)
location2 = location2CreateResponse.data.stock_location
})

it("should list stock locations", async () => {
const listLocationsResponse = await api.get(
"/admin/stock-locations",
adminHeaders
)

expect(listLocationsResponse.status).toEqual(200)
expect(listLocationsResponse.data.stock_locations).toEqual([
expect.objectContaining(location1),
expect.objectContaining(location2),
])
})

it("should filter stock locations by name", async () => {
const listLocationsResponse = await api.get(
"/admin/stock-locations?name=Test%20Location%201",
adminHeaders
)

expect(listLocationsResponse.status).toEqual(200)
expect(listLocationsResponse.data.stock_locations).toEqual([
expect.objectContaining(location1),
])
})

it("should filter stock locations by partial name with q parameter", async () => {
const listLocationsResponse = await api.get(
"/admin/stock-locations?q=ation%201",
adminHeaders
)

expect(listLocationsResponse.status).toEqual(200)
expect(listLocationsResponse.data.stock_locations).toEqual([
expect.objectContaining(location1),
])
})

it("should filter stock locations on sales_channel_id", async () => {
const remoteLinkService = appContainer.resolve(
ContainerRegistrationKeys.REMOTE_LINK
)

await remoteLinkService.create([
{
[Modules.SALES_CHANNEL]: {
sales_channel_id: "default",
},
[Modules.STOCK_LOCATION]: {
stock_location_id: location1.id,
},
},
])

const listLocationsResponse = await api.get(
"/admin/stock-locations?sales_channel_id=default",
adminHeaders
)

expect(listLocationsResponse.status).toEqual(200)
expect(listLocationsResponse.data.stock_locations).toEqual([
expect.objectContaining(location1),
])
})
})

describe("Update stock locations", () => {
let stockLocationId

Expand Down
13 changes: 13 additions & 0 deletions packages/medusa/src/api-v2/admin/stock-locations/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as QueryConfig from "./query-config"

import {
AdminGetStockLocationsLocationParams,
AdminGetStockLocationsParams,
AdminPostStockLocationsLocationParams,
AdminPostStockLocationsLocationReq,
AdminPostStockLocationsParams,
Expand All @@ -10,6 +11,7 @@ import {
import { transformBody, transformQuery } from "../../../api/middlewares"

import { MiddlewareRoute } from "../../../types/middlewares"
import { applySalesChannelsFilter } from "./utils/apply-sales-channel-filter"
import { authenticate } from "../../../utils/authenticate-middleware"

export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
Expand All @@ -29,6 +31,17 @@ export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
),
],
},
{
method: ["GET"],
matcher: "/admin/stock-locations",
middlewares: [
transformQuery(
AdminGetStockLocationsParams,
QueryConfig.listTransformQueryConfig
),
applySalesChannelsFilter(),
],
},
{
method: ["POST"],
matcher: "/admin/stock-locations/:id",
Expand Down
22 changes: 22 additions & 0 deletions packages/medusa/src/api-v2/admin/stock-locations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,25 @@ export const POST = async (

res.status(200).json({ stock_location })
}

export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)

const { rows: stock_locations } = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: "stock_locations",
variables: {
filters: req.filterableFields,
order: req.listConfig.order,
skip: req.listConfig.skip,
take: req.listConfig.take,
},
fields: req.remoteQueryConfig.fields,
})
)

res.status(200).json({
stock_locations,
...req.remoteQueryConfig.pagination,
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"

import { AdminGetStockLocationsParams } from "../validators"
import { MedusaRequest } from "../../../../types/routing"
import { Modules } from "@medusajs/modules-sdk"
import { NextFunction } from "express"

export function applySalesChannelsFilter() {
return async (req: MedusaRequest, _, next: NextFunction) => {
const filterableFields: AdminGetStockLocationsParams = req.filterableFields

if (!filterableFields.sales_channel_id) {
return next()
}

const salesChannelIds = Array.isArray(filterableFields.sales_channel_id)
? filterableFields.sales_channel_id
: [filterableFields.sales_channel_id]

delete filterableFields.sales_channel_id

const remoteLinkService = req.scope.resolve(
ContainerRegistrationKeys.REMOTE_LINK
)

const stockLocationSalesChannelLinkModuleService =
await remoteLinkService.getLinkModule(
Modules.SALES_CHANNEL,
"sales_channel_id",
Modules.STOCK_LOCATION,
"stock_location_id"
)

const stockLocationSalesChannelLinks =
await stockLocationSalesChannelLinkModuleService.list(
{ sales_channel_id: salesChannelIds },
{}
)

filterableFields.id = stockLocationSalesChannelLinks.map(
(link) => link.stock_location_id
)

return next()
}
}
49 changes: 49 additions & 0 deletions packages/medusa/src/api-v2/admin/stock-locations/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,55 @@ export class AdminPostStockLocationsReq {

export class AdminPostStockLocationsParams extends FindParams {}

/**
* Parameters used to filter and configure the pagination of the retrieved stock locations.
*/
export class AdminGetStockLocationsParams extends extendedFindParamsMixin({
limit: 20,
offset: 0,
}) {
/**
* Search term to search stock location names.
*/
@IsString()
@IsOptional()
q?: string

/**
* IDs to filter stock locations by.
*/
@IsOptional()
@IsType([String, [String]])
id?: string | string[]

/**
* Names to filter stock locations by.
*/
@IsOptional()
@IsType([String, [String]])
name?: string | string[]

/**
* Filter stock locations by the ID of their associated addresses.
*/
@IsOptional()
@IsType([String, [String]])
address_id?: string | string[]

/**
* Filter stock locations by the ID of their associated sales channels.
*/
@IsOptional()
@IsType([String, [String]])
sales_channel_id?: string | string[]

/**
* The field to sort the data by. By default, the sort order is ascending. To change the order to descending, prefix the field name with `-`.
*/
@IsString()
@IsOptional()
order?: string
}
/**
* The attributes of a stock location address to create or update.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/stock-location-next/src/repositories/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
export { StockLocationRepository } from "./stock-location"
52 changes: 52 additions & 0 deletions packages/stock-location-next/src/repositories/stock-location.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Context, DAL } from "@medusajs/types"

import { StockLocation } from "@models"
import { mikroOrmBaseRepositoryFactory } from "@medusajs/utils"

export class StockLocationRepository extends mikroOrmBaseRepositoryFactory<StockLocation>(
StockLocation
) {
async find(
findOptions: DAL.FindOptions<StockLocation & { q?: string }> = {
where: {},
},
context: Context
): Promise<StockLocation[]> {
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}

this.applyFreeTextSearchFilters<StockLocation>(
findOptions_,
this.getFreeTextSearchConstraints
)

return await super.find(findOptions_, context)
}

async findAndCount(
findOptions: DAL.FindOptions<StockLocation & { q?: string }> = {
where: {},
},
context: Context
): Promise<[StockLocation[], number]> {
const findOptions_ = { ...findOptions }
findOptions_.options ??= {}

this.applyFreeTextSearchFilters<StockLocation>(
findOptions_,
this.getFreeTextSearchConstraints
)

return await super.findAndCount(findOptions_, context)
}

protected getFreeTextSearchConstraints(q: string) {
return [
{
name: {
$ilike: `%${q}%`,
},
},
]
}
}
2 changes: 1 addition & 1 deletion packages/stock-location-next/src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default as StockLocationModuleService } from "./stock-location"
export { default as StockLocationModuleService } from "./stock-location-module"

0 comments on commit 18438a6

Please sign in to comment.