feat(core-flows, medusa): add create stock location endpoint for api-v2 (#6787)
This commit is contained in:
6
.changeset/six-flies-dress.md
Normal file
6
.changeset/six-flies-dress.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@medusajs/core-flows": patch
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
feat(core-flows, medusa): add create stock location endpoint for api-v2
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
adminHeaders,
|
||||
createAdminUser,
|
||||
} from "../../../../helpers/create-admin-user"
|
||||
|
||||
import { IStockLocationServiceNext } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
const { medusaIntegrationTestRunner } = require("medusa-test-utils")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
env: {
|
||||
MEDUSA_FF_MEDUSA_V2: true,
|
||||
},
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
let appContainer
|
||||
let service: IStockLocationServiceNext
|
||||
|
||||
beforeEach(async () => {
|
||||
appContainer = getContainer()
|
||||
|
||||
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
||||
|
||||
service = appContainer.resolve(ModuleRegistrationName.STOCK_LOCATION)
|
||||
})
|
||||
|
||||
describe("create stock location", () => {
|
||||
it("should create a stock location with a name and address", async () => {
|
||||
const address = {
|
||||
address_1: "Test Address",
|
||||
country_code: "US",
|
||||
}
|
||||
const location = {
|
||||
name: "Test Location",
|
||||
}
|
||||
|
||||
const response = await api.post(
|
||||
"/admin/stock-locations",
|
||||
{
|
||||
...location,
|
||||
address,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.stock_location).toEqual(
|
||||
expect.objectContaining({
|
||||
...location,
|
||||
address: expect.objectContaining(address),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -67,8 +67,10 @@ module.exports = {
|
||||
resolve: "@medusajs/cache-inmemory",
|
||||
options: { ttl: 0 }, // Cache disabled
|
||||
},
|
||||
[Modules.STOCK_LOCATION]: true,
|
||||
[Modules.INVENTORY]: true,
|
||||
[Modules.STOCK_LOCATION]: {
|
||||
resolve: "@medusajs/stock-location-next",
|
||||
options: {},
|
||||
},
|
||||
[Modules.PRODUCT]: true,
|
||||
[Modules.PRICING]: true,
|
||||
[Modules.PROMOTION]: true,
|
||||
|
||||
@@ -17,6 +17,7 @@ export * from "./promotion"
|
||||
export * from "./region"
|
||||
export * from "./sales-channel"
|
||||
export * from "./shipping-options"
|
||||
export * from "./stock-location"
|
||||
export * from "./store"
|
||||
export * from "./tax"
|
||||
export * from "./user"
|
||||
|
||||
2
packages/core-flows/src/stock-location/index.ts
Normal file
2
packages/core-flows/src/stock-location/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./steps"
|
||||
export * from "./workflows"
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
CreateStockLocationInput,
|
||||
IStockLocationServiceNext,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const createStockLocationsStepId = "create-stock-locations"
|
||||
export const createStockLocations = createStep(
|
||||
createStockLocationsStepId,
|
||||
async (data: CreateStockLocationInput[], { container }) => {
|
||||
const stockLocationService = container.resolve<IStockLocationServiceNext>(
|
||||
ModuleRegistrationName.STOCK_LOCATION
|
||||
)
|
||||
|
||||
const created = await stockLocationService.create(data)
|
||||
|
||||
return new StepResponse(
|
||||
created,
|
||||
created.map((i) => i.id)
|
||||
)
|
||||
},
|
||||
async (createdStockLocationIds, { container }) => {
|
||||
if (!createdStockLocationIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const stockLocationService = container.resolve(
|
||||
ModuleRegistrationName.STOCK_LOCATION
|
||||
)
|
||||
|
||||
await stockLocationService.delete(createdStockLocationIds)
|
||||
}
|
||||
)
|
||||
1
packages/core-flows/src/stock-location/steps/index.ts
Normal file
1
packages/core-flows/src/stock-location/steps/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./create-stock-locations"
|
||||
@@ -0,0 +1,18 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
|
||||
import { CreateStockLocationInput } from "@medusajs/types"
|
||||
import { createStockLocations } from "../steps"
|
||||
|
||||
interface WorkflowInput {
|
||||
locations: CreateStockLocationInput[]
|
||||
}
|
||||
|
||||
export const createStockLocationsWorkflowId = "create-stock-locations-workflow"
|
||||
export const createStockLocationsWorkflow = createWorkflow(
|
||||
createStockLocationsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>) => {
|
||||
const locations = createStockLocations(input.locations)
|
||||
|
||||
return locations
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./create-stock-locations"
|
||||
@@ -0,0 +1,29 @@
|
||||
import * as QueryConfig from "./query-config"
|
||||
|
||||
import {
|
||||
AdminPostStockLocationsParams,
|
||||
AdminPostStockLocationsReq,
|
||||
} from "./validators"
|
||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||
|
||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||
|
||||
export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: "ALL",
|
||||
matcher: "/admin/stock-locations*",
|
||||
middlewares: [authenticate("admin", ["session", "bearer", "api-key"])],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/stock-locations",
|
||||
middlewares: [
|
||||
transformBody(AdminPostStockLocationsReq),
|
||||
transformQuery(
|
||||
AdminPostStockLocationsParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
export const defaultAdminStockLocationFields = [
|
||||
"id",
|
||||
"name",
|
||||
"metadata",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"address.id",
|
||||
"address.address_1",
|
||||
"address.address_2",
|
||||
"address.city",
|
||||
"address.country_code",
|
||||
"address.phone",
|
||||
"address.province",
|
||||
"address.postal_code",
|
||||
"address.metadata",
|
||||
]
|
||||
|
||||
export const retrieveTransformQueryConfig = {
|
||||
defaults: defaultAdminStockLocationFields,
|
||||
allowed: defaultAdminStockLocationFields,
|
||||
isList: false,
|
||||
}
|
||||
|
||||
export const listTransformQueryConfig = {
|
||||
...retrieveTransformQueryConfig,
|
||||
isList: true,
|
||||
}
|
||||
32
packages/medusa/src/api-v2/admin/stock-locations/route.ts
Normal file
32
packages/medusa/src/api-v2/admin/stock-locations/route.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
|
||||
|
||||
import { AdminPostStockLocationsReq } from "./validators"
|
||||
import { createStockLocationsWorkflow } from "@medusajs/core-flows"
|
||||
|
||||
// Create stock location
|
||||
export const POST = async (
|
||||
req: MedusaRequest<AdminPostStockLocationsReq>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const { result } = await createStockLocationsWorkflow(req.scope).run({
|
||||
input: { locations: [req.validatedBody] },
|
||||
})
|
||||
|
||||
const [stock_location] = await remoteQuery(
|
||||
remoteQueryObjectFromString({
|
||||
entryPoint: "stock_locations",
|
||||
variables: {
|
||||
id: result[0].id,
|
||||
},
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
)
|
||||
|
||||
res.status(200).json({ stock_location })
|
||||
}
|
||||
139
packages/medusa/src/api-v2/admin/stock-locations/validators.ts
Normal file
139
packages/medusa/src/api-v2/admin/stock-locations/validators.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import {
|
||||
DateComparisonOperator,
|
||||
FindParams,
|
||||
NumericalComparisonOperator,
|
||||
StringComparisonOperator,
|
||||
extendedFindParamsMixin,
|
||||
} from "../../../types/common"
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEmail,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { Transform, Type } from "class-transformer"
|
||||
|
||||
import { IsType } from "../../../utils"
|
||||
|
||||
/**
|
||||
* @schema AdminPostStockLocationsReqAddress
|
||||
* type: object
|
||||
* required:
|
||||
* - address_1
|
||||
* - country_code
|
||||
* properties:
|
||||
* address_1:
|
||||
* type: string
|
||||
* description: Stock location address
|
||||
* example: 35, Jhon Doe Ave
|
||||
* address_2:
|
||||
* type: string
|
||||
* description: Stock location address' complement
|
||||
* example: apartment 4432
|
||||
* company:
|
||||
* type: string
|
||||
* description: Stock location address' company
|
||||
* city:
|
||||
* type: string
|
||||
* description: Stock location address' city
|
||||
* example: Mexico city
|
||||
* country_code:
|
||||
* description: "The two character ISO code for the country."
|
||||
* type: string
|
||||
* externalDocs:
|
||||
* url: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
|
||||
* description: See a list of codes.
|
||||
* phone:
|
||||
* type: string
|
||||
* description: Stock location address' phone number
|
||||
* example: +1 555 61646
|
||||
* postal_code:
|
||||
* type: string
|
||||
* description: Stock location address' postal code
|
||||
* example: HD3-1G8
|
||||
* province:
|
||||
* type: string
|
||||
* description: Stock location address' province
|
||||
* example: Sinaloa
|
||||
*/
|
||||
class StockLocationAddress {
|
||||
@IsString()
|
||||
address_1: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address_2?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
company?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
city?: string
|
||||
|
||||
@IsString()
|
||||
country_code: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
postal_code?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
province?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema AdminPostStockLocationsReq
|
||||
* type: object
|
||||
* description: "The details of the stock location to create."
|
||||
* required:
|
||||
* - name
|
||||
* properties:
|
||||
* name:
|
||||
* description: the name of the stock location
|
||||
* type: string
|
||||
* address_id:
|
||||
* description: the ID of an existing stock location address to associate with the stock location. Only required if `address` is not provided.
|
||||
* type: string
|
||||
* metadata:
|
||||
* type: object
|
||||
* description: An optional key-value map with additional details
|
||||
* example: {car: "white"}
|
||||
* externalDocs:
|
||||
* description: "Learn about the metadata attribute, and how to delete and update it."
|
||||
* url: "https://docs.medusajs.com/development/entities/overview#metadata-attribute"
|
||||
* address:
|
||||
* description: A new stock location address to create and associate with the stock location. Only required if `address_id` is not provided.
|
||||
* $ref: "#/components/schemas/StockLocationAddressInput"
|
||||
*/
|
||||
export class AdminPostStockLocationsReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Transform(({ value }: { value: string }) => value?.trim())
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => StockLocationAddress)
|
||||
address?: StockLocationAddress
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address_id?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export class AdminPostStockLocationsParams extends FindParams {}
|
||||
@@ -15,6 +15,7 @@ import { adminProductRoutesMiddlewares } from "./admin/products/middlewares"
|
||||
import { adminPromotionRoutesMiddlewares } from "./admin/promotions/middlewares"
|
||||
import { adminRegionRoutesMiddlewares } from "./admin/regions/middlewares"
|
||||
import { adminSalesChannelRoutesMiddlewares } from "./admin/sales-channels/middlewares"
|
||||
import { adminStockLocationRoutesMiddlewares } from "./admin/stock-locations/middlewares"
|
||||
import { adminStoreRoutesMiddlewares } from "./admin/stores/middlewares"
|
||||
import { adminTaxRateRoutesMiddlewares } from "./admin/tax-rates/middlewares"
|
||||
import { adminTaxRegionRoutesMiddlewares } from "./admin/tax-regions/middlewares"
|
||||
@@ -57,5 +58,6 @@ export const config: MiddlewaresConfig = {
|
||||
...adminPricingRoutesMiddlewares,
|
||||
...adminFulfillmentRoutesMiddlewares,
|
||||
...adminSalesChannelRoutesMiddlewares,
|
||||
...adminStockLocationRoutesMiddlewares,
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user