feat(core-flows, medusa): add create stock location endpoint for api-v2 (#6787)
This commit is contained in:
1 parent
247ca3c3fa
commit
68b9812aa1
14 files changed
+355
-2
No files matched your search
@@ -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",
|
resolve: "@medusajs/cache-inmemory",
|
||||||
options: { ttl: 0 }, // Cache disabled
|
options: { ttl: 0 }, // Cache disabled
|
||||||
},
|
},
|
||||||
[Modules.STOCK_LOCATION]: true,
|
[Modules.STOCK_LOCATION]: {
|
||||||
[Modules.INVENTORY]: true,
|
resolve: "@medusajs/stock-location-next",
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
[Modules.PRODUCT]: true,
|
[Modules.PRODUCT]: true,
|
||||||
[Modules.PRICING]: true,
|
[Modules.PRICING]: true,
|
||||||
[Modules.PROMOTION]: true,
|
[Modules.PROMOTION]: true,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export * from "./promotion"
|
|||||||
export * from "./region"
|
export * from "./region"
|
||||||
export * from "./sales-channel"
|
export * from "./sales-channel"
|
||||||
export * from "./shipping-options"
|
export * from "./shipping-options"
|
||||||
|
export * from "./stock-location"
|
||||||
export * from "./store"
|
export * from "./store"
|
||||||
export * from "./tax"
|
export * from "./tax"
|
||||||
export * from "./user"
|
export * from "./user"
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -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 })
|
||||||
|
}
|
||||||
@@ -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 { adminPromotionRoutesMiddlewares } from "./admin/promotions/middlewares"
|
||||||
import { adminRegionRoutesMiddlewares } from "./admin/regions/middlewares"
|
import { adminRegionRoutesMiddlewares } from "./admin/regions/middlewares"
|
||||||
import { adminSalesChannelRoutesMiddlewares } from "./admin/sales-channels/middlewares"
|
import { adminSalesChannelRoutesMiddlewares } from "./admin/sales-channels/middlewares"
|
||||||
|
import { adminStockLocationRoutesMiddlewares } from "./admin/stock-locations/middlewares"
|
||||||
import { adminStoreRoutesMiddlewares } from "./admin/stores/middlewares"
|
import { adminStoreRoutesMiddlewares } from "./admin/stores/middlewares"
|
||||||
import { adminTaxRateRoutesMiddlewares } from "./admin/tax-rates/middlewares"
|
import { adminTaxRateRoutesMiddlewares } from "./admin/tax-rates/middlewares"
|
||||||
import { adminTaxRegionRoutesMiddlewares } from "./admin/tax-regions/middlewares"
|
import { adminTaxRegionRoutesMiddlewares } from "./admin/tax-regions/middlewares"
|
||||||
@@ -57,5 +58,6 @@ export const config: MiddlewaresConfig = {
|
|||||||
...adminPricingRoutesMiddlewares,
|
...adminPricingRoutesMiddlewares,
|
||||||
...adminFulfillmentRoutesMiddlewares,
|
...adminFulfillmentRoutesMiddlewares,
|
||||||
...adminSalesChannelRoutesMiddlewares,
|
...adminSalesChannelRoutesMiddlewares,
|
||||||
|
...adminStockLocationRoutesMiddlewares,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user