feat: creates support for swaps on the storefront (#355)

This commit is contained in:
Sebastian Rindom
2021-08-30 12:14:56 +02:00
committed by GitHub
parent bd1e115696
commit ae82cfc70a
20 changed files with 2122 additions and 1170 deletions
@@ -1,8 +1,73 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultFields, defaultRelations } from "./"
/**
* @oas [post] /swaps
* operationId: PostSwaps
* summary: Create a Swap
* description: "Creates a Swap on an Order by providing some items to return along with some items to send back"
* requestBody:
* content:
* application/json:
* schema:
* properties:
* order_id:
* type: string
* description: The id of the Order to create the Swap for.
* return_items:
* description: "The items to include in the Return."
* type: array
* items:
* properties:
* item_id:
* description: The id of the Line Item from the Order.
* type: string
* quantity:
* description: The quantity to return.
* type: integer
* return_shipping_option:
* type: string
* description: The id of the Shipping Option to create the Shipping Method from.
* additional_items:
* description: "The items to exchange the returned items to."
* type: array
* items:
* properties:
* variant_id:
* description: The id of the Product Variant to send.
* type: string
* quantity:
* description: The quantity to send of the variant.
* type: integer
* tags:
* - Swap
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* swap:
* $ref: "#/components/schemas/swap"
*/
export default async (req, res) => {
const schema = Validator.object().keys({
cart_id: Validator.string().required(),
order_id: Validator.string().required(),
return_items: Validator.array()
.items({
item_id: Validator.string().required(),
quantity: Validator.number().required(),
reason_id: Validator.string().optional(),
note: Validator.string().optional(),
})
.required(),
return_shipping_option: Validator.string().optional(),
additional_items: Validator.array().items({
variant_id: Validator.string().required(),
quantity: Validator.number().required(),
}),
})
const { value, error } = schema.validate(req.body)
@@ -10,17 +75,151 @@ export default async (req, res) => {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
const swapService = req.scope.resolve("swapService")
const idempotencyKeyService = req.scope.resolve("idempotencyKeyService")
const headerKey = req.get("Idempotency-Key") || ""
let idempotencyKey
try {
idempotencyKey = await idempotencyKeyService.initializeRequest(
headerKey,
req.method,
req.params,
req.path
)
} catch (error) {
res.status(409).send("Failed to create idempotency key")
return
}
res.setHeader("Access-Control-Expose-Headers", "Idempotency-Key")
res.setHeader("Idempotency-Key", idempotencyKey.idempotency_key)
try {
const swap = await swapService.retrieveByCartId(value.cart_id)
const data = await swapService.registerCartCompletion(
swap.id,
value.cart_id
)
const orderService = req.scope.resolve("orderService")
const swapService = req.scope.resolve("swapService")
const returnService = req.scope.resolve("returnService")
res.status(200).json({ swap: data })
} catch (err) {
throw err
let inProgress = true
let err = false
while (inProgress) {
switch (idempotencyKey.recovery_point) {
case "started": {
const { key, error } = await idempotencyKeyService.workStage(
idempotencyKey.idempotency_key,
async (manager) => {
const order = await orderService
.withTransaction(manager)
.retrieve(value.order_id, {
select: ["refunded_total", "total"],
relations: ["items", "swaps", "swaps.additional_items"],
})
let returnShipping
if (value.return_shipping_option) {
returnShipping = {
option_id: value.return_shipping_option,
}
}
const swap = await swapService
.withTransaction(manager)
.create(
order,
value.return_items,
value.additional_items,
returnShipping,
{
idempotency_key: idempotencyKey.idempotency_key,
no_notification: true,
}
)
await swapService.withTransaction(manager).createCart(swap.id)
const returnOrder = await returnService
.withTransaction(manager)
.retrieveBySwap(swap.id)
await returnService
.withTransaction(manager)
.fulfill(returnOrder.id)
return {
recovery_point: "swap_created",
}
}
)
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break
}
case "swap_created": {
const { key, error } = await idempotencyKeyService.workStage(
idempotencyKey.idempotency_key,
async (manager) => {
const swaps = await swapService.list({
idempotency_key: idempotencyKey.idempotency_key,
})
if (!swaps.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Swap not found"
)
}
const swap = await swapService.retrieve(swaps[0].id, {
select: defaultFields,
relations: defaultRelations,
})
return {
response_code: 200,
response_body: { swap },
}
}
)
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break
}
case "finished": {
inProgress = false
break
}
default:
idempotencyKey = await idempotencyKeyService.update(
idempotencyKey.idempotency_key,
{
recovery_point: "finished",
response_code: 500,
response_body: { message: "Unknown recovery point" },
}
)
break
}
}
if (err) {
throw err
}
res.status(idempotencyKey.response_code).json(idempotencyKey.response_body)
} catch (error) {
throw error
}
}
@@ -3,7 +3,7 @@ import middlewares from "../../../middlewares"
const route = Router()
export default app => {
export default (app) => {
app.use("/swaps", route)
route.get(
@@ -14,3 +14,29 @@ export default app => {
return app
}
export const defaultRelations = [
"order",
"additional_items",
"return_order",
"fulfillments",
"payment",
"shipping_address",
"shipping_methods",
"cart",
]
export const defaultFields = [
"id",
"fulfillment_status",
"payment_status",
"order_id",
"difference_due",
"shipping_address_id",
"cart_id",
"confirmed_at",
"created_at",
"updated_at",
"deleted_at",
"metadata",
"idempotency_key",
]