chore(medusa): Add transactions in mutating actions in store domain (#1858)

This commit is contained in:
Adrien de Peretti authored and GitHub committed 2022-07-28 11:48:55 +02:00
1 parent 3fbe8d7d08
commit 7cb8095ed4
26 files changed
+437 -287

No files matched your search

@@ -3,6 +3,7 @@ import jwt from "jsonwebtoken"
import AuthService from "../../../../services/auth" import AuthService from "../../../../services/auth"
import CustomerService from "../../../../services/customer" import CustomerService from "../../../../services/customer"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { EntityManager } from "typeorm"
/** /**
* @oas [post] /auth * @oas [post] /auth
@@ -28,10 +29,13 @@ export default async (req, res) => {
const validated = await validator(StorePostAuthReq, req.body) const validated = await validator(StorePostAuthReq, req.body)
const authService: AuthService = req.scope.resolve("authService") const authService: AuthService = req.scope.resolve("authService")
const result = await authService.authenticateCustomer( const manager: EntityManager = req.scope.resolve("manager")
validated.email, const result = await manager.transaction(async (transactionManager) => {
validated.password return await authService
) .withTransaction(transactionManager)
.authenticateCustomer(validated.email, validated.password)
})
if (!result.success) { if (!result.success) {
res.sendStatus(401) res.sendStatus(401)
return return
@@ -31,17 +31,22 @@ export default async (req, res) => {
const idempotencyKeyService: IdempotencyKeyService = req.scope.resolve( const idempotencyKeyService: IdempotencyKeyService = req.scope.resolve(
"idempotencyKeyService" "idempotencyKeyService"
) )
const manager: EntityManager = req.scope.resolve("manager")
const headerKey = req.get("Idempotency-Key") || "" const headerKey = req.get("Idempotency-Key") || ""
let idempotencyKey: IdempotencyKey let idempotencyKey!: IdempotencyKey
try { try {
idempotencyKey = await idempotencyKeyService.initializeRequest( await manager.transaction(async (transactionManager) => {
headerKey, idempotencyKey = await idempotencyKeyService
req.method, .withTransaction(transactionManager)
req.params, .initializeRequest(
req.path headerKey,
) req.method,
req.params,
req.path
)
})
} catch (error) { } catch (error) {
console.log(error) console.log(error)
res.status(409).send("Failed to create idempotency key") res.status(409).send("Failed to create idempotency key")
@@ -59,42 +64,46 @@ export default async (req, res) => {
while (inProgress) { while (inProgress) {
switch (idempotencyKey.recovery_point) { switch (idempotencyKey.recovery_point) {
case "started": { case "started": {
const { key, error } = await idempotencyKeyService.workStage( await manager.transaction(async (transactionManager) => {
idempotencyKey.idempotency_key, const { key, error } = await idempotencyKeyService
async (manager: EntityManager) => { .withTransaction(transactionManager)
const cart = await cartService.withTransaction(manager).retrieve( .workStage(
id, idempotencyKey.idempotency_key,
{ async (manager: EntityManager) => {
relations: ["items", "items.adjustments"], const cart = await cartService.withTransaction(manager).retrieve(
select: [ id,
"total", {
"subtotal", relations: ["items", "items.adjustments"],
"tax_total", select: [
"discount_total", "total",
"shipping_total", "subtotal",
"gift_card_total", "tax_total",
], "discount_total",
}, "shipping_total",
{ force_taxes: true } "gift_card_total",
],
},
{ force_taxes: true }
)
const data = await decorateLineItemsWithTotals(cart, req, {
force_taxes: true,
})
return {
response_code: 200,
response_body: { cart: data },
}
}
) )
const data = await decorateLineItemsWithTotals(cart, req, { if (error) {
force_taxes: true, inProgress = false
}) err = error
} else {
return { idempotencyKey = key
response_code: 200,
response_body: { cart: data },
}
} }
) })
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break break
} }
@@ -104,14 +113,18 @@ export default async (req, res) => {
} }
default: default:
idempotencyKey = await idempotencyKeyService.update( await manager.transaction(async (transactionManager) => {
idempotencyKey.idempotency_key, idempotencyKey = await idempotencyKeyService
{ .withTransaction(transactionManager)
recovery_point: "finished", .update(
response_code: 500, idempotencyKey.idempotency_key,
response_body: { message: "Unknown recovery point" }, {
} recovery_point: "finished",
) response_code: 500,
response_body: { message: "Unknown recovery point" },
}
)
})
break break
} }
} }
@@ -1,6 +1,7 @@
import { ICartCompletionStrategy } from "../../../../interfaces" import { ICartCompletionStrategy } from "../../../../interfaces"
import { IdempotencyKeyService } from "../../../../services" import { IdempotencyKeyService } from "../../../../services"
import { IdempotencyKey } from "../../../../models/idempotency-key" import { IdempotencyKey } from "../../../../models/idempotency-key"
import { EntityManager } from "typeorm";
/** /**
* @oas [post] /carts/{id}/complete * @oas [post] /carts/{id}/complete
@@ -50,12 +51,15 @@ export default async (req, res) => {
let idempotencyKey: IdempotencyKey let idempotencyKey: IdempotencyKey
try { try {
idempotencyKey = await idempotencyKeyService.initializeRequest( const manager: EntityManager = req.scope.resolve("manager")
headerKey, idempotencyKey = await manager.transaction(async (transactionManager) => {
req.method, return await idempotencyKeyService.withTransaction(transactionManager).initializeRequest(
req.params, headerKey,
req.path req.method,
) req.params,
req.path
)
})
} catch (error) { } catch (error) {
console.log(error) console.log(error)
res.status(409).send("Failed to create idempotency key") res.status(409).send("Failed to create idempotency key")
@@ -1,6 +1,7 @@
import { defaultStoreCartFields, defaultStoreCartRelations } from "." import { defaultStoreCartFields, defaultStoreCartRelations } from "."
import { CartService } from "../../../../services" import { CartService } from "../../../../services"
import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals" import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals"
import { EntityManager } from "typeorm";
/** /**
* @oas [post] /carts/{id}/payment-sessions * @oas [post] /carts/{id}/payment-sessions
@@ -26,7 +27,10 @@ export default async (req, res) => {
const cartService: CartService = req.scope.resolve("cartService") const cartService: CartService = req.scope.resolve("cartService")
await cartService.setPaymentSessions(id) const manager: EntityManager = req.scope.resolve("manager")
await manager.transaction(async (transactionManager) => {
return await cartService.withTransaction(transactionManager).setPaymentSessions(id)
})
const cart = await cartService.retrieve(id, { const cart = await cartService.retrieve(id, {
select: defaultStoreCartFields, select: defaultStoreCartFields,
@@ -1,6 +1,7 @@
import { Request } from "express" import { Request } from "express"
import { TotalsService } from "../../../../services" import { TotalsService } from "../../../../services"
import { Cart, LineItem } from "../../../../models" import { Cart, LineItem } from "../../../../models"
import { EntityManager } from "typeorm";
export const decorateLineItemsWithTotals = async ( export const decorateLineItemsWithTotals = async (
cart: Cart, cart: Cart,
@@ -10,15 +11,20 @@ export const decorateLineItemsWithTotals = async (
const totalsService: TotalsService = req.scope.resolve("totalsService") const totalsService: TotalsService = req.scope.resolve("totalsService")
if (cart.items && cart.region) { if (cart.items && cart.region) {
const items = await Promise.all( const manager: EntityManager = req.scope.resolve("manager")
cart.items.map(async (item: LineItem) => { const items = await manager.transaction(async (transactionManager) => {
const itemTotals = await totalsService.getLineItemTotals(item, cart, { return await Promise.all(
include_tax: options.force_taxes || cart.region.automatic_taxes, cart.items.map(async (item: LineItem) => {
}) const itemTotals = await totalsService
.withTransaction(transactionManager)
.getLineItemTotals(item, cart, {
include_tax: options.force_taxes || cart.region.automatic_taxes,
})
return Object.assign(item, itemTotals) return Object.assign(item, itemTotals)
}) })
) )
})
return Object.assign(cart, { items }) return Object.assign(cart, { items })
} }
@@ -1,6 +1,7 @@
import { defaultStoreCartFields, defaultStoreCartRelations } from "." import { defaultStoreCartFields, defaultStoreCartRelations } from "."
import { CartService } from "../../../../services" import { CartService } from "../../../../services"
import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals" import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals"
import { EntityManager } from "typeorm";
/** /**
* @oas [delete] /carts/{id}/payment-sessions/{provider_id} * @oas [delete] /carts/{id}/payment-sessions/{provider_id}
@@ -27,7 +28,11 @@ export default async (req, res) => {
const cartService: CartService = req.scope.resolve("cartService") const cartService: CartService = req.scope.resolve("cartService")
await cartService.deletePaymentSession(id, provider_id) const manager: EntityManager = req.scope.resolve("manager")
await manager.transaction(async (transactionManager) => {
return await cartService.withTransaction(transactionManager).deletePaymentSession(id, provider_id)
})
const cart = await cartService.retrieve(id, { const cart = await cartService.retrieve(id, {
select: defaultStoreCartFields, select: defaultStoreCartFields,
relations: defaultStoreCartRelations, relations: defaultStoreCartRelations,
@@ -1,5 +1,6 @@
import { CartService } from "../../../../services" import { CartService } from "../../../../services"
import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals" import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals"
import { EntityManager } from "typeorm";
/** /**
* @oas [post] /carts/{id}/payment-sessions/{provider_id}/refresh * @oas [post] /carts/{id}/payment-sessions/{provider_id}/refresh
@@ -26,7 +27,10 @@ export default async (req, res) => {
const cartService: CartService = req.scope.resolve("cartService") const cartService: CartService = req.scope.resolve("cartService")
await cartService.refreshPaymentSession(id, provider_id) const manager: EntityManager = req.scope.resolve("manager")
await manager.transaction(async (transactionManager) => {
return await cartService.withTransaction(transactionManager).refreshPaymentSession(id, provider_id)
})
const cart = await cartService.retrieve(id, { const cart = await cartService.retrieve(id, {
select: [ select: [
"subtotal", "subtotal",
@@ -3,6 +3,7 @@ import { defaultStoreCartFields, defaultStoreCartRelations } from "."
import { CartService } from "../../../../services" import { CartService } from "../../../../services"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals" import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals"
import { EntityManager } from "typeorm";
/** /**
* @oas [post] /carts/{id}/payment-session * @oas [post] /carts/{id}/payment-session
@@ -34,8 +35,12 @@ export default async (req, res) => {
const cartService: CartService = req.scope.resolve("cartService") const cartService: CartService = req.scope.resolve("cartService")
let cart = await cartService.setPaymentSession(id, validated.provider_id) const manager: EntityManager = req.scope.resolve("manager")
cart = await cartService.retrieve(id, { await manager.transaction(async (transactionManager) => {
return await cartService.withTransaction(transactionManager).setPaymentSession(id, validated.provider_id)
})
const cart = await cartService.retrieve(id, {
select: defaultStoreCartFields, select: defaultStoreCartFields,
relations: defaultStoreCartRelations, relations: defaultStoreCartRelations,
}) })
@@ -11,6 +11,7 @@ import { CartService } from "../../../../services"
import { AddressPayload } from "../../../../types/common" import { AddressPayload } from "../../../../types/common"
import { IsType } from "../../../../utils/validators/is-type" import { IsType } from "../../../../utils/validators/is-type"
import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals" import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals"
import { EntityManager } from "typeorm";
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"; import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators";
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"; import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels";
@@ -89,16 +90,20 @@ export default async (req, res) => {
const validated = req.validatedBody as StorePostCartsCartReq const validated = req.validatedBody as StorePostCartsCartReq
const cartService: CartService = req.scope.resolve("cartService") const cartService: CartService = req.scope.resolve("cartService")
await cartService.update(id, validated) const manager: EntityManager = req.scope.resolve("manager")
const updated = await cartService.retrieve(id, { await manager.transaction(async (transactionManager) => {
relations: ["payment_sessions", "shipping_methods"], await cartService.withTransaction(transactionManager).update(id, validated)
const updated = await cartService.withTransaction(transactionManager).retrieve(id, {
relations: ["payment_sessions", "shipping_methods"],
})
if (updated.payment_sessions?.length && !validated.region_id) {
await cartService.withTransaction(transactionManager).setPaymentSessions(id)
}
}) })
if (updated.payment_sessions?.length && !validated.region_id) {
await cartService.setPaymentSessions(id)
}
const cart = await cartService.retrieve(id, { const cart = await cartService.retrieve(id, {
select: defaultStoreCartFields, select: defaultStoreCartFields,
relations: defaultStoreCartRelations, relations: defaultStoreCartRelations,
@@ -43,7 +43,7 @@ export default async (req, res) => {
if (validated.quantity === 0) { if (validated.quantity === 0) {
await cartService.withTransaction(m).removeLineItem(id, line_id) await cartService.withTransaction(m).removeLineItem(id, line_id)
} else { } else {
const cart = await cartService.retrieve(id, { relations: ["items"] }) const cart = await cartService.withTransaction(m).retrieve(id, { relations: ["items"] })
const existing = cart.items.find((i) => i.id === line_id) const existing = cart.items.find((i) => i.id === line_id)
if (!existing) { if (!existing) {
@@ -3,6 +3,7 @@ import { defaultStoreCartFields, defaultStoreCartRelations } from "."
import { CartService } from "../../../../services" import { CartService } from "../../../../services"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals" import { decorateLineItemsWithTotals } from "./decorate-line-items-with-totals"
import { EntityManager } from "typeorm";
/** /**
* @oas [post] /carts/{id}/payment-sessions/{provider_id} * @oas [post] /carts/{id}/payment-sessions/{provider_id}
@@ -35,8 +36,11 @@ export default async (req, res) => {
const cartService: CartService = req.scope.resolve("cartService") const cartService: CartService = req.scope.resolve("cartService")
await cartService.setPaymentSession(id, provider_id) const manager: EntityManager = req.scope.resolve("manager")
await cartService.updatePaymentSession(id, validated.data) await manager.transaction(async (transactionManager) => {
await cartService.withTransaction(transactionManager).setPaymentSession(id, provider_id)
await cartService.withTransaction(transactionManager).updatePaymentSession(id, validated.data)
})
const cart = await cartService.retrieve(id, { const cart = await cartService.retrieve(id, {
select: defaultStoreCartFields, select: defaultStoreCartFields,
@@ -4,6 +4,7 @@ import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
import CustomerService from "../../../../services/customer" import CustomerService from "../../../../services/customer"
import { AddressCreatePayload } from "../../../../types/common" import { AddressCreatePayload } from "../../../../types/common"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { EntityManager } from "typeorm"
/** /**
* @oas [post] /customers/me/addresses * @oas [post] /customers/me/addresses
@@ -44,8 +45,14 @@ export default async (req, res) => {
const customerService: CustomerService = req.scope.resolve("customerService") const customerService: CustomerService = req.scope.resolve("customerService")
let customer = await customerService.addAddress(id, validated.address) const manager: EntityManager = req.scope.resolve("manager")
customer = await customerService.retrieve(id, { await manager.transaction(async (transactionManager) => {
return await customerService
.withTransaction(transactionManager)
.addAddress(id, validated.address)
})
const customer = await customerService.retrieve(id, {
relations: defaultStoreCustomersRelations, relations: defaultStoreCustomersRelations,
select: defaultStoreCustomersFields, select: defaultStoreCustomersFields,
}) })
@@ -4,6 +4,7 @@ import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
import { Customer } from "../../../.." import { Customer } from "../../../.."
import CustomerService from "../../../../services/customer" import CustomerService from "../../../../services/customer"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { EntityManager } from "typeorm"
/** /**
* @oas [post] /customers * @oas [post] /customers
@@ -32,7 +33,14 @@ export default async (req, res) => {
const validated = await validator(StorePostCustomersReq, req.body) const validated = await validator(StorePostCustomersReq, req.body)
const customerService: CustomerService = req.scope.resolve("customerService") const customerService: CustomerService = req.scope.resolve("customerService")
let customer: Customer = await customerService.create(validated) const manager: EntityManager = req.scope.resolve("manager")
let customer: Customer = await manager.transaction(
async (transactionManager) => {
return await customerService
.withTransaction(transactionManager)
.create(validated)
}
)
// Add JWT to cookie // Add JWT to cookie
const { const {
@@ -1,5 +1,6 @@
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "." import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
import CustomerService from "../../../../services/customer" import CustomerService from "../../../../services/customer"
import { EntityManager } from "typeorm"
/** /**
* @oas [delete] /customers/me/addresses/{address_id} * @oas [delete] /customers/me/addresses/{address_id}
@@ -28,7 +29,12 @@ export default async (req, res) => {
const customerService: CustomerService = req.scope.resolve("customerService") const customerService: CustomerService = req.scope.resolve("customerService")
await customerService.removeAddress(id, address_id) const manager: EntityManager = req.scope.resolve("manager")
await manager.transaction(async (transactionManager) => {
return await customerService
.withTransaction(transactionManager)
.removeAddress(id, address_id)
})
const customer = await customerService.retrieve(id, { const customer = await customerService.retrieve(id, {
relations: defaultStoreCustomersRelations, relations: defaultStoreCustomersRelations,
@@ -1,6 +1,7 @@
import { IsEmail } from "class-validator" import { IsEmail } from "class-validator"
import CustomerService from "../../../../services/customer" import CustomerService from "../../../../services/customer"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { EntityManager } from "typeorm"
/** /**
* @oas [post] /customers/password-token * @oas [post] /customers/password-token
@@ -28,7 +29,12 @@ export default async (req, res) => {
const customer = await customerService.retrieveByEmail(validated.email) const customer = await customerService.retrieveByEmail(validated.email)
// Will generate a token and send it to the customer via an email provider // Will generate a token and send it to the customer via an email provider
await customerService.generateResetPasswordToken(customer.id) const manager: EntityManager = req.scope.resolve("manager")
await manager.transaction(async (transactionManager) => {
return await customerService
.withTransaction(transactionManager)
.generateResetPasswordToken(customer.id)
})
res.sendStatus(204) res.sendStatus(204)
} }
@@ -2,6 +2,7 @@ import { IsEmail, IsString } from "class-validator"
import jwt, { JwtPayload } from "jsonwebtoken" import jwt, { JwtPayload } from "jsonwebtoken"
import CustomerService from "../../../../services/customer" import CustomerService from "../../../../services/customer"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { EntityManager } from "typeorm"
/** /**
* @oas [post] /customers/reset-password * @oas [post] /customers/reset-password
@@ -44,8 +45,13 @@ export default async (req, res) => {
return return
} }
await customerService.update(customer.id, { const manager: EntityManager = req.scope.resolve("manager")
password: validated.password, await manager.transaction(async (transactionManager) => {
return await customerService
.withTransaction(transactionManager)
.update(customer.id, {
password: validated.password,
})
}) })
customer = await customerService.retrieve(customer.id) customer = await customerService.retrieve(customer.id)
@@ -2,6 +2,7 @@ import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
import CustomerService from "../../../../services/customer" import CustomerService from "../../../../services/customer"
import { AddressPayload } from "../../../../types/common" import { AddressPayload } from "../../../../types/common"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { EntityManager } from "typeorm"
/** /**
* @oas [post] /customers/me/addresses/{address_id} * @oas [post] /customers/me/addresses/{address_id}
@@ -45,7 +46,12 @@ export default async (req, res) => {
"customerService" "customerService"
) as CustomerService ) as CustomerService
await customerService.updateAddress(id, address_id, validated) const manager: EntityManager = req.scope.resolve("manager")
await manager.transaction(async (transactionManager) => {
return await customerService
.withTransaction(transactionManager)
.updateAddress(id, address_id, validated)
})
const customer = await customerService.retrieve(id, { const customer = await customerService.retrieve(id, {
relations: defaultStoreCustomersRelations, relations: defaultStoreCustomersRelations,
@@ -4,6 +4,7 @@ import CustomerService from "../../../../services/customer"
import { AddressPayload } from "../../../../types/common" import { AddressPayload } from "../../../../types/common"
import { IsType } from "../../../../utils/validators/is-type" import { IsType } from "../../../../utils/validators/is-type"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { EntityManager } from "typeorm"
/** /**
* @oas [post] /customers/me * @oas [post] /customers/me
@@ -56,7 +57,12 @@ export default async (req, res) => {
const validated = await validator(StorePostCustomersCustomerReq, req.body) const validated = await validator(StorePostCustomersCustomerReq, req.body)
const customerService: CustomerService = req.scope.resolve("customerService") const customerService: CustomerService = req.scope.resolve("customerService")
await customerService.update(id, validated) const manager: EntityManager = req.scope.resolve("manager")
await manager.transaction(async (transactionManager) => {
return await customerService
.withTransaction(transactionManager)
.update(id, validated)
})
const customer = await customerService.retrieve(id, { const customer = await customerService.retrieve(id, {
relations: defaultStoreCustomersRelations, relations: defaultStoreCustomersRelations,
@@ -14,6 +14,7 @@ import IdempotencyKeyService from "../../../../services/idempotency-key"
import OrderService from "../../../../services/order" import OrderService from "../../../../services/order"
import ReturnService from "../../../../services/return" import ReturnService from "../../../../services/return"
import { validator } from "../../../../utils/validator" import { validator } from "../../../../utils/validator"
import { EntityManager } from "typeorm";
/** /**
* @oas [post] /returns * @oas [post] /returns
@@ -72,17 +73,20 @@ export default async (req, res) => {
const idempotencyKeyService: IdempotencyKeyService = req.scope.resolve( const idempotencyKeyService: IdempotencyKeyService = req.scope.resolve(
"idempotencyKeyService" "idempotencyKeyService"
) )
const manager: EntityManager = req.scope.resolve("manager")
const headerKey = req.get("Idempotency-Key") || "" const headerKey = req.get("Idempotency-Key") || ""
let idempotencyKey let idempotencyKey
try { try {
idempotencyKey = await idempotencyKeyService.initializeRequest( await manager.transaction(async (transactionManager) => {
headerKey, idempotencyKey = await idempotencyKeyService.withTransaction(transactionManager).initializeRequest(
req.method, headerKey,
req.params, req.method,
req.path req.params,
) req.path
)
})
} catch (error) { } catch (error) {
res.status(409).send("Failed to create idempotency key") res.status(409).send("Failed to create idempotency key")
return return
@@ -102,91 +106,99 @@ export default async (req, res) => {
while (inProgress) { while (inProgress) {
switch (idempotencyKey.recovery_point) { switch (idempotencyKey.recovery_point) {
case "started": { case "started": {
const { key, error } = await idempotencyKeyService.workStage( await manager.transaction(async (transactionManager) => {
idempotencyKey.idempotency_key, const { key, error } = await idempotencyKeyService
async (manager) => { .withTransaction(transactionManager)
const order = await orderService .workStage(
.withTransaction(manager) idempotencyKey.idempotency_key,
.retrieve(returnDto.order_id, { async (manager) => {
select: ["refunded_total", "total"], const order = await orderService
relations: ["items"], .withTransaction(manager)
}) .retrieve(returnDto.order_id, {
select: ["refunded_total", "total"],
relations: ["items"],
})
const returnObj: any = { const returnObj: any = {
order_id: returnDto.order_id, order_id: returnDto.order_id,
idempotency_key: idempotencyKey.idempotency_key, idempotency_key: idempotencyKey.idempotency_key,
items: returnDto.items, items: returnDto.items,
} }
if (returnDto.return_shipping) { if (returnDto.return_shipping) {
returnObj.shipping_method = returnDto.return_shipping returnObj.shipping_method = returnDto.return_shipping
} }
const createdReturn = await returnService const createdReturn = await returnService
.withTransaction(manager) .withTransaction(manager)
.create(returnObj) .create(returnObj)
if (returnDto.return_shipping) { if (returnDto.return_shipping) {
await returnService await returnService
.withTransaction(manager) .withTransaction(manager)
.fulfill(createdReturn.id) .fulfill(createdReturn.id)
} }
await eventBus await eventBus
.withTransaction(manager) .withTransaction(manager)
.emit("order.return_requested", { .emit("order.return_requested", {
id: returnDto.order_id, id: returnDto.order_id,
return_id: createdReturn.id, return_id: createdReturn.id,
}) })
return { return {
recovery_point: "return_requested", recovery_point: "return_requested",
} }
}
)
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
} }
) })
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break break
} }
case "return_requested": { case "return_requested": {
const { key, error } = await idempotencyKeyService.workStage( await manager.transaction(async (transactionManager) => {
idempotencyKey.idempotency_key, const { key, error } = await idempotencyKeyService
async (manager) => { .withTransaction(transactionManager)
let ret = await returnService.withTransaction(manager).list( .workStage(
{ idempotencyKey.idempotency_key,
idempotency_key: idempotencyKey.idempotency_key, async (manager) => {
}, let ret = await returnService.withTransaction(manager).list(
{ {
relations: ["items", "items.reason"], idempotency_key: idempotencyKey.idempotency_key,
},
{
relations: ["items", "items.reason"],
}
)
if (!ret.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Return not found`
)
}
ret = ret[0]
return {
response_code: 200,
response_body: { return: ret },
}
} }
) )
if (!ret.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Return not found`
)
}
ret = ret[0]
return { if (error) {
response_code: 200, inProgress = false
response_body: { return: ret }, err = error
} } else {
idempotencyKey = key
} }
) })
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break break
} }
@@ -196,14 +208,18 @@ export default async (req, res) => {
} }
default: default:
idempotencyKey = await idempotencyKeyService.update( await manager.transaction(async (transactionManager) => {
idempotencyKey.idempotency_key, idempotencyKey = await idempotencyKeyService
{ .withTransaction(transactionManager)
recovery_point: "finished", .update(
response_code: 500, idempotencyKey.idempotency_key,
response_body: { message: "Unknown recovery point" }, {
} recovery_point: "finished",
) response_code: 500,
response_body: { message: "Unknown recovery point" },
}
)
})
break break
} }
} }
@@ -93,17 +93,17 @@ export default async (req, res) => {
const idempotencyKeyService: IdempotencyKeyService = req.scope.resolve( const idempotencyKeyService: IdempotencyKeyService = req.scope.resolve(
"idempotencyKeyService" "idempotencyKeyService"
) )
const manager: EntityManager = req.scope.resolve("manager")
const headerKey = req.get("Idempotency-Key") || "" const headerKey = req.get("Idempotency-Key") || ""
let idempotencyKey let idempotencyKey
try { try {
idempotencyKey = await idempotencyKeyService.initializeRequest( await manager.transaction(async (transactionManager) => {
headerKey, idempotencyKey = await idempotencyKeyService
req.method, .withTransaction(transactionManager)
req.params, .initializeRequest(headerKey, req.method, req.params, req.path)
req.path })
)
} catch (error) { } catch (error) {
res.status(409).send("Failed to create idempotency key") res.status(409).send("Failed to create idempotency key")
return return
@@ -122,101 +122,108 @@ export default async (req, res) => {
while (inProgress) { while (inProgress) {
switch (idempotencyKey.recovery_point) { switch (idempotencyKey.recovery_point) {
case "started": { case "started": {
const { key, error } = await idempotencyKeyService.workStage( await manager.transaction(async (transactionManager) => {
idempotencyKey.idempotency_key, const { key, error } = await idempotencyKeyService
async (manager) => { .withTransaction(transactionManager)
const order = await orderService .workStage(idempotencyKey.idempotency_key, async (manager) => {
.withTransaction(manager) const order = await orderService
.retrieve(swapDto.order_id, { .withTransaction(manager)
select: ["refunded_total", "total"], .retrieve(swapDto.order_id, {
relations: [ select: ["refunded_total", "total"],
"items", relations: [
"items.tax_lines", "items",
"swaps", "items.tax_lines",
"swaps.additional_items", "swaps",
"swaps.additional_items.tax_lines", "swaps.additional_items",
], "swaps.additional_items.tax_lines",
}) ],
})
let returnShipping let returnShipping
if (swapDto.return_shipping_option) { if (swapDto.return_shipping_option) {
returnShipping = { returnShipping = {
option_id: swapDto.return_shipping_option, option_id: swapDto.return_shipping_option,
}
}
const swap = await swapService
.withTransaction(manager)
.create(
order,
swapDto.return_items,
swapDto.additional_items,
returnShipping,
{
idempotency_key: idempotencyKey.idempotency_key,
no_notification: true,
} }
) }
await swapService.withTransaction(manager).createCart(swap.id) const swap = await swapService
const returnOrder = await returnService .withTransaction(manager)
.withTransaction(manager) .create(
.retrieveBySwap(swap.id) order,
swapDto.return_items,
swapDto.additional_items,
returnShipping,
{
idempotency_key: idempotencyKey.idempotency_key,
no_notification: true,
}
)
await returnService.withTransaction(manager).fulfill(returnOrder.id) await swapService.withTransaction(manager).createCart(swap.id)
const returnOrder = await returnService
.withTransaction(manager)
.retrieveBySwap(swap.id)
return { await returnService
recovery_point: "swap_created", .withTransaction(manager)
} .fulfill(returnOrder.id)
return {
recovery_point: "swap_created",
}
})
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
} }
) })
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break break
} }
case "swap_created": { case "swap_created": {
const { key, error } = await idempotencyKeyService.workStage( await manager.transaction(async (transactionManager) => {
idempotencyKey.idempotency_key, const { key, error } = await idempotencyKeyService
async (transactionManager: EntityManager) => { .withTransaction(transactionManager)
const swaps = await swapService .workStage(
.withTransaction(transactionManager) idempotencyKey.idempotency_key,
.list({ async (transactionManager: EntityManager) => {
idempotency_key: idempotencyKey.idempotency_key, const swaps = await swapService
}) .withTransaction(transactionManager)
.list({
idempotency_key: idempotencyKey.idempotency_key,
})
if (!swaps.length) { if (!swaps.length) {
throw new MedusaError( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
"Swap not found" "Swap not found"
) )
} }
const swap = await swapService const swap = await swapService
.withTransaction(transactionManager) .withTransaction(transactionManager)
.retrieve(swaps[0].id, { .retrieve(swaps[0].id, {
select: defaultStoreSwapFields, select: defaultStoreSwapFields,
relations: defaultStoreSwapRelations, relations: defaultStoreSwapRelations,
}) })
return { return {
response_code: 200, response_code: 200,
response_body: { swap }, response_body: { swap },
} }
}
)
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
} }
) })
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break break
} }
@@ -226,14 +233,15 @@ export default async (req, res) => {
} }
default: default:
idempotencyKey = await idempotencyKeyService.update( await manager.transaction(async (transactionManager) => {
idempotencyKey.idempotency_key, idempotencyKey = await idempotencyKeyService
{ .withTransaction(transactionManager)
recovery_point: "finished", .update(idempotencyKey.idempotency_key, {
response_code: 500, recovery_point: "finished",
response_body: { message: "Unknown recovery point" }, response_code: 500,
} response_body: { message: "Unknown recovery point" },
) })
})
break break
} }
} }
+1 -1
View File
@@ -1,4 +1,4 @@
import { EntityRepository, Repository } from "typeorm" import { EntityRepository, Repository } from "typeorm"
import { ClaimOrder } from "../models/claim-order" import { ClaimOrder } from "../models/claim-order"
@EntityRepository(ClaimOrder) @EntityRepository(ClaimOrder)
@@ -117,6 +117,9 @@ describe("TaxProviderService", () => {
test("success", async () => { test("success", async () => {
container.taxRateService = { container.taxRateService = {
withTransaction: function () {
return this
},
listByProduct: jest.fn(() => Promise.resolve([])), listByProduct: jest.fn(() => Promise.resolve([])),
} }
@@ -155,6 +158,9 @@ describe("TaxProviderService", () => {
test("success - without product rates", async () => { test("success - without product rates", async () => {
container.taxRateService = { container.taxRateService = {
withTransaction: function () {
return this
},
listByProduct: jest.fn(() => Promise.resolve([])), listByProduct: jest.fn(() => Promise.resolve([])),
} }
@@ -193,6 +199,9 @@ describe("TaxProviderService", () => {
test("success - with product rates", async () => { test("success - with product rates", async () => {
container.taxRateService = { container.taxRateService = {
withTransaction: function () {
return this
},
listByProduct: jest.fn(() => listByProduct: jest.fn(() =>
Promise.resolve([ Promise.resolve([
{ {
@@ -1,12 +1,12 @@
import { MedusaError } from "medusa-core-utils" import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import { v4 } from "uuid" import { v4 } from "uuid"
import { TransactionBaseService } from "../interfaces"
const KEY_LOCKED_TIMEOUT = 1000 const KEY_LOCKED_TIMEOUT = 1000
class IdempotencyKeyService extends BaseService { class IdempotencyKeyService extends TransactionBaseService {
constructor({ manager, idempotencyKeyRepository }) { constructor({ manager, idempotencyKeyRepository }) {
super() super({ manager, idempotencyKeyRepository })
/** @private @constant {EntityManager} */ /** @private @constant {EntityManager} */
this.manager_ = manager this.manager_ = manager
+6 -4
View File
@@ -197,10 +197,12 @@ class SwapService extends BaseService {
} }
if (cartRelations || cartSelects) { if (cartRelations || cartSelects) {
const cart = await this.cartService_.retrieve(swap.cart_id, { const cart = await this.cartService_
select: cartSelects, .withTransaction(this.manager_)
relations: cartRelations, .retrieve(swap.cart_id, {
}) select: cartSelects,
relations: cartRelations,
})
swap.cart = cart swap.cart = cart
} }
+5 -3
View File
@@ -385,9 +385,11 @@ class TaxProviderService extends BaseService {
} }
let toReturn: TaxServiceRate[] = [] let toReturn: TaxServiceRate[] = []
const productRates = await this.taxRateService_.listByProduct(productId, { const productRates = await this.taxRateService_
region_id: region.id, .withTransaction(this.manager_)
}) .listByProduct(productId, {
region_id: region.id,
})
if (productRates.length > 0) { if (productRates.length > 0) {
toReturn = productRates.map((pr) => { toReturn = productRates.map((pr) => {
+21 -7
View File
@@ -1,6 +1,10 @@
import { MedusaError } from "medusa-core-utils" import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces" import { BaseService } from "medusa-interfaces"
import { ITaxCalculationStrategy, TaxCalculationContext } from "../interfaces" import {
ITaxCalculationStrategy,
TaxCalculationContext,
TransactionBaseService,
} from "../interfaces"
import { Cart } from "../models/cart" import { Cart } from "../models/cart"
import { Discount } from "../models/discount" import { Discount } from "../models/discount"
import { DiscountRuleType } from "../models/discount-rule" import { DiscountRuleType } from "../models/discount-rule"
@@ -18,6 +22,7 @@ import {
SubtotalOptions, SubtotalOptions,
} from "../types/totals" } from "../types/totals"
import TaxProviderService from "./tax-provider" import TaxProviderService from "./tax-provider"
import { EntityManager } from "typeorm"
type ShippingMethodTotals = { type ShippingMethodTotals = {
price: number price: number
@@ -60,6 +65,7 @@ type GetLineItemTotalOptions = {
type TotalsServiceProps = { type TotalsServiceProps = {
taxProviderService: TaxProviderService taxProviderService: TaxProviderService
taxCalculationStrategy: ITaxCalculationStrategy taxCalculationStrategy: ITaxCalculationStrategy
manager: EntityManager
} }
type GetTotalsOptions = { type GetTotalsOptions = {
@@ -83,18 +89,27 @@ type CalculationContextOptions = {
* A service that calculates total and subtotals for orders, carts etc.. * A service that calculates total and subtotals for orders, carts etc..
* @implements {BaseService} * @implements {BaseService}
*/ */
class TotalsService extends BaseService { class TotalsService extends TransactionBaseService<TotalsService> {
protected manager_: EntityManager
protected transactionManager_: EntityManager
private taxProviderService_: TaxProviderService private taxProviderService_: TaxProviderService
private taxCalculationStrategy_: ITaxCalculationStrategy private taxCalculationStrategy_: ITaxCalculationStrategy
constructor({ constructor({
taxProviderService, taxProviderService,
taxCalculationStrategy, taxCalculationStrategy,
manager,
}: TotalsServiceProps) { }: TotalsServiceProps) {
super() super({
taxProviderService,
taxCalculationStrategy,
manager,
})
this.taxProviderService_ = taxProviderService this.taxProviderService_ = taxProviderService
this.taxCalculationStrategy_ = taxCalculationStrategy this.taxCalculationStrategy_ = taxCalculationStrategy
this.manager_ = manager
} }
/** /**
@@ -757,10 +772,9 @@ class TotalsService extends BaseService {
} }
taxLines = lineItem.tax_lines taxLines = lineItem.tax_lines
} else { } else {
const orderLines = await this.taxProviderService_.getTaxLines( const orderLines = await this.taxProviderService_
cartOrOrder.items, .withTransaction(this.manager_)
calculationContext .getTaxLines(cartOrOrder.items, calculationContext)
)
taxLines = orderLines.filter((ol) => { taxLines = orderLines.filter((ol) => {
if ("item_id" in ol) { if ("item_id" in ol) {