feat: Refresh payment collection + delete session (#6594)
### What Add workflow for refreshing a payment collection. The idea is that on all cart updates, we want two things to happen (in the context of payments) 1. the currently active payment sessions should be destroyed, and 2. the payment collection should be updated with the new cart total. We do this to ensure that we always collect the correct payment amount. From a customer perspective, this would mean that every time something on the cart is updated, the customer would need to enter their payment details anew. To me, this is a good tradeoff to avoid inconsistencies with payment collection. Additionally, I updated the Payment Module interface with `upsert` and `updated` following our established convention. ### Note This PR depends on a fix to the `remoteJoiner` that @carlos-r-l-rodrigues is working on. Update: Fix merged in #6602 Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
74
packages/utils/src/common/__tests__/deep-copy.spec.ts
Normal file
74
packages/utils/src/common/__tests__/deep-copy.spec.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { deepCopy } from "../deep-copy"
|
||||
|
||||
class TestA {
|
||||
prop1: any
|
||||
prop2: any
|
||||
|
||||
constructor(prop1: any, prop2: any) {
|
||||
this.prop1 = prop1
|
||||
this.prop2 = prop2
|
||||
}
|
||||
}
|
||||
|
||||
class TestWrapper {
|
||||
prop1: any
|
||||
prop2: any
|
||||
|
||||
constructor(prop1: any, prop2: any) {
|
||||
this.prop1 = prop1
|
||||
this.prop2 = prop2
|
||||
}
|
||||
|
||||
factory() {
|
||||
return new TestA(deepCopy(this.prop1), deepCopy(this.prop2))
|
||||
}
|
||||
}
|
||||
|
||||
class TestWrapperWithoutDeepCopy {
|
||||
prop1: any
|
||||
prop2: any
|
||||
|
||||
constructor(prop1: any, prop2: any) {
|
||||
this.prop1 = prop1
|
||||
this.prop2 = prop2
|
||||
}
|
||||
|
||||
factory() {
|
||||
return new TestA(this.prop1, this.prop2)
|
||||
}
|
||||
}
|
||||
|
||||
describe("deepCopy", () => {
|
||||
it("should deep copy an object", () => {
|
||||
const prop1 = {
|
||||
prop1: 1,
|
||||
}
|
||||
|
||||
const prop2 = {
|
||||
prop1: 3,
|
||||
}
|
||||
|
||||
const wrapperWithoutDeepCopy = new TestWrapperWithoutDeepCopy(prop1, prop2)
|
||||
let factory1 = wrapperWithoutDeepCopy.factory()
|
||||
let factory2 = wrapperWithoutDeepCopy.factory()
|
||||
|
||||
factory1.prop1.prop1 = 2
|
||||
|
||||
expect(wrapperWithoutDeepCopy.prop1).toEqual({ prop1: 2 })
|
||||
expect(factory1.prop1).toEqual({ prop1: 2 })
|
||||
expect(factory2.prop1).toEqual({ prop1: 2 })
|
||||
|
||||
prop1.prop1 = 4
|
||||
prop2.prop1 = 4
|
||||
|
||||
const wrapper = new TestWrapper(prop1, prop2)
|
||||
factory1 = wrapper.factory()
|
||||
factory2 = wrapper.factory()
|
||||
|
||||
factory1.prop1.prop1 = 2
|
||||
|
||||
expect(wrapper.prop1).toEqual({ prop1: 4 })
|
||||
expect(factory1.prop1).toEqual({ prop1: 2 })
|
||||
expect(factory2.prop1).toEqual({ prop1: 4 })
|
||||
})
|
||||
})
|
||||
40
packages/utils/src/common/deep-copy.ts
Normal file
40
packages/utils/src/common/deep-copy.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { isObject } from "./is-object"
|
||||
|
||||
/**
|
||||
* In most casees, JSON.parse(JSON.stringify(obj)) is enough to deep copy an object.
|
||||
* But in some cases, it's not enough. For example, if the object contains a function or a proxy, it will be lost after JSON.parse(JSON.stringify(obj)).
|
||||
* Furthermore, structuredClone is not present in all environments, such as with jest so we need to use a custom deepCopy function.
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
export function deepCopy<T extends Record<any, any> = Record<any, any>>(
|
||||
obj: T | T[]
|
||||
): T | T[] {
|
||||
if (typeof structuredClone != "undefined") {
|
||||
return structuredClone(obj)
|
||||
}
|
||||
|
||||
if (obj === null || typeof obj !== "object") {
|
||||
return obj
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
const copy: any[] = []
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
copy[i] = deepCopy(obj[i])
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
if (isObject(obj)) {
|
||||
const copy: Record<any, any> = {}
|
||||
for (let attr in obj) {
|
||||
if (obj.hasOwnProperty(attr)) {
|
||||
copy[attr] = deepCopy(obj[attr])
|
||||
}
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export * from "./create-container-like"
|
||||
export * from "./create-psql-index-helper"
|
||||
export * from "./deduplicate"
|
||||
export * from "./deep-equal-obj"
|
||||
export * from "./deep-copy"
|
||||
export * from "./errors"
|
||||
export * from "./generate-entity-id"
|
||||
export * from "./generate-linkable-keys-map"
|
||||
|
||||
@@ -149,7 +149,7 @@ export const mikroOrmSerializer = async <TOutput extends object>(
|
||||
): Promise<TOutput> => {
|
||||
options ??= {}
|
||||
|
||||
const data_ = Array.isArray(data) ? data : [data]
|
||||
const data_ = (Array.isArray(data) ? data : [data]).filter(Boolean)
|
||||
|
||||
const forSerialization: unknown[] = []
|
||||
const notForSerialization: unknown[] = []
|
||||
|
||||
Reference in New Issue
Block a user