fix: Addresses breaking change from library class-transformer (#835)

Co-authored-by: Philip Korsholm <philip.korsholm@hotmail.com>
This commit is contained in:
Oliver Windall Juhl
2021-11-22 13:39:38 +01:00
committed by GitHub
co-authored by Philip Korsholm
parent 59761163c2
commit f387b4919f
9 changed files with 183 additions and 690 deletions
@@ -1,355 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultRelations, defaultFields } from "./"
/**
* @oas [post] /order/{id}/claims
* operationId: "PostOrdersOrderClaims"
* summary: "Create a Claim"
* description: "Creates a Claim."
* parameters:
* - (path) id=* {string} The id of the Order.
* requestBody:
* content:
* application/json:
* schema:
* properties:
* type:
* description: "The type of the Claim. This will determine how the Claim is treated: `replace` Claims will result in a Fulfillment with new items being created, while a `refund` Claim will refund the amount paid for the claimed items."
* type: string
* enum:
* - replace
* - refund
* claim_items:
* description: The Claim Items that the Claim will consist of.
* type: array
* items:
* properties:
* item_id:
* description: The id of the Line Item that will be claimed.
* type: string
* quantity:
* description: The number of items that will be returned
* type: integer
* note:
* description: Short text describing the Claim Item in further detail.
* type: string
* reason:
* description: The reason for the Claim
* type: string
* enum:
* - missing_item
* - wrong_item
* - production_failure
* - other
* tags:
* description: A list o tags to add to the Claim Item
* type: array
* items:
* type: string
* images:
* description: A list of image URL's that will be associated with the Claim
* items:
* type: string
* return_shipping:
* description: Optional details for the Return Shipping Method, if the items are to be sent back.
* type: object
* properties:
* option_id:
* type: string
* description: The id of the Shipping Option to create the Shipping Method from.
* price:
* type: integer
* description: The price to charge for the Shipping Method.
* additional_items:
* description: The new items to send to the Customer when the Claim type is Replace.
* type: array
* items:
* properties:
* variant_id:
* description: The id of the Product Variant to ship.
* type: string
* quantity:
* description: The quantity of the Product Variant to ship.
* type: integer
* shipping_methods:
* description: The Shipping Methods to send the additional Line Items with.
* type: array
* items:
* properties:
* id:
* description: The id of an existing Shipping Method
* type: string
* option_id:
* description: The id of the Shipping Option to create a Shipping Method from
* type: string
* price:
* description: The price to charge for the Shipping Method
* type: integer
* shipping_address:
* type: object
* description: "An optional shipping address to send the claim to. Defaults to the parent order's shipping address"
* refund_amount:
* description: The amount to refund the Customer when the Claim type is `refund`.
* type: integer
* no_notification:
* description: If set to true no notification will be send related to this Claim.
* type: boolean
* metadata:
* description: An optional set of key-value pairs to hold additional information.
* type: object
* tags:
* - Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* order:
* $ref: "#/components/schemas/order"
*/
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
type: Validator.string()
.valid("replace", "refund")
.required(),
claim_items: Validator.array()
.items({
item_id: Validator.string().required(),
quantity: Validator.number().required(),
note: Validator.string().optional(),
reason: Validator.string().valid(
"missing_item",
"wrong_item",
"production_failure",
"other"
),
tags: Validator.array().items(Validator.string()),
images: Validator.array().items(Validator.string()),
})
.required(),
return_shipping: Validator.object()
.keys({
option_id: Validator.string().optional(),
price: Validator.number()
.integer()
.optional(),
})
.optional(),
additional_items: Validator.array()
.items({
variant_id: Validator.string().required(),
quantity: Validator.number().required(),
})
.optional(),
shipping_methods: Validator.array()
.items({
id: Validator.string().optional(),
option_id: Validator.string().optional(),
price: Validator.number()
.integer()
.optional(),
})
.optional(),
refund_amount: Validator.number()
.integer()
.optional(),
shipping_address: Validator.object().optional(),
no_notification: Validator.boolean().optional(),
metadata: Validator.object().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
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 orderService = req.scope.resolve("orderService")
const claimService = req.scope.resolve("claimService")
const returnService = req.scope.resolve("returnService")
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(id, {
relations: ["items", "discounts", "discounts.rule"],
})
await claimService.withTransaction(manager).create({
idempotency_key: idempotencyKey.idempotency_key,
order,
type: value.type,
shipping_address: value.shipping_address,
claim_items: value.claim_items,
return_shipping: value.return_shipping,
additional_items: value.additional_items,
shipping_methods: value.shipping_methods,
no_notification: value.no_notification,
metadata: value.metadata,
})
return {
recovery_point: "claim_created",
}
}
)
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break
}
case "claim_created": {
const { key, error } = await idempotencyKeyService.workStage(
idempotencyKey.idempotency_key,
async (manager) => {
let claim = await claimService.withTransaction(manager).list({
idempotency_key: idempotencyKey.idempotency_key,
})
if (!claim.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Claim not found`
)
}
claim = claim[0]
if (claim.type === "refund") {
await claimService
.withTransaction(manager)
.processRefund(claim.id)
}
return {
recovery_point: "refund_handled",
}
}
)
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break
}
case "refund_handled": {
const { key, error } = await idempotencyKeyService.workStage(
idempotencyKey.idempotency_key,
async (manager) => {
let order = await orderService
.withTransaction(manager)
.retrieve(id, {
relations: ["items", "discounts"],
})
let claim = await claimService.withTransaction(manager).list(
{
idempotency_key: idempotencyKey.idempotency_key,
},
{
relations: ["return_order"],
}
)
if (!claim.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Claim not found`
)
}
claim = claim[0]
if (claim.return_order) {
await returnService
.withTransaction(manager)
.fulfill(claim.return_order.id)
}
order = await orderService.withTransaction(manager).retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
return {
response_code: 200,
response_body: { order },
}
}
)
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
}
}
@@ -169,12 +169,7 @@ export default async (req, res) => {
const order = await orderService
.withTransaction(manager)
.retrieve(id, {
relations: [
"items",
"cart",
"cart.discounts",
"cart.discounts.rule",
],
relations: ["items", "discounts", "discounts.rule"],
})
await claimService.withTransaction(manager).create({
+2 -2
View File
@@ -1,4 +1,4 @@
import { ClassConstructor, plainToClass } from "class-transformer"
import { ClassConstructor, plainToInstance } from "class-transformer"
import { validate, ValidationError, ValidatorOptions } from "class-validator"
import { MedusaError } from "medusa-core-utils"
@@ -22,7 +22,7 @@ export async function validator<T, V>(
plain: V,
config: ValidatorOptions = {}
): Promise<T> {
const toValidate = plainToClass(typedClass, plain)
const toValidate = plainToInstance(typedClass, plain)
// @ts-ignore
const errors = await validate(toValidate, {
whitelist: true,