feat(utils): Fix big number decorator and cleanup (#6473)

**What**
- Fix big number decorator and cleanup
This commit is contained in:
Adrien de Peretti
2024-02-22 16:58:41 +00:00
committed by GitHub
parent 598ee6f49c
commit 36a61658f9
14 changed files with 757 additions and 773 deletions
@@ -0,0 +1,47 @@
import { MikroOrmBigNumberProperty } from "../big-number-field"
import { BigNumberRawValue } from "@medusajs/types"
import { BigNumber } from "../../../totals/big-number"
describe("@MikroOrmBigNumberProperty", () => {
it("should correctly assign and update BigNumber values", () => {
class TestAmount {
@MikroOrmBigNumberProperty()
amount: BigNumber | number
raw_amount: BigNumberRawValue
}
const testAmount = new TestAmount()
expect(testAmount.amount).toBeUndefined()
expect(testAmount.raw_amount).toBeUndefined()
testAmount.amount = 100
expect(testAmount.amount).toEqual(100)
expect((testAmount as any).amount_).toEqual(100)
expect(testAmount.raw_amount).toEqual({
value: "100.00000000000000000",
precision: 20,
})
// Update the amount
testAmount.amount = 200
expect(testAmount.amount).toEqual(200)
expect((testAmount as any).amount_).toEqual(200)
expect(testAmount.raw_amount).toEqual({
value: "200.00000000000000000",
precision: 20,
})
// Update with big number
testAmount.amount = new BigNumber(300, { precision: 5 })
expect(testAmount.amount).toEqual(300)
expect((testAmount as any).amount_).toEqual(300)
expect(testAmount.raw_amount).toEqual({ value: "300.00", precision: 5 })
})
})
@@ -1,78 +1,54 @@
import { BigNumber } from "../../totals/big-number"
import { Property } from "@mikro-orm/core"
import { BigNumberInput } from "@medusajs/types"
const bigNumberFields = new WeakMap<
object,
{ prop: string; options: { nullable?: boolean } }[]
>()
export function MikroOrmBigNumberProperty(
options: Parameters<typeof Property>[0] & {
rawColumnName?: string
} = {}
) {
return function (target: any, columnName: string) {
const targetColumn = columnName + "_"
const rawColumnName = options.rawColumnName ?? `raw_${columnName}`
export function BigNumberField(options: { nullable?: boolean } = {}) {
return function (target: any, prop: string) {
const entity = target.constructor
if (!bigNumberFields.has(entity)) {
bigNumberFields.set(entity, [])
}
Object.defineProperty(target, columnName, {
get() {
return this[targetColumn]
},
set(value: BigNumberInput) {
let bigNumber: BigNumber
if (value instanceof BigNumber) {
bigNumber = value
} else if (this[rawColumnName]) {
const precision = this[rawColumnName].precision
this[rawColumnName].value = new BigNumber(value, {
precision,
}).raw!.value
bigNumber = new BigNumber(this[rawColumnName])
} else {
bigNumber = new BigNumber(value)
}
if (prop.startsWith("raw_")) {
const suggestedPropName = prop.replace("raw_", "")
throw new Error(
`BigNumberField decorator has to be used on the property "${suggestedPropName}" and ${prop} typed as BigNumberRawValue.`
)
}
this[targetColumn] = bigNumber.numeric
this[rawColumnName] = bigNumber.raw
},
})
bigNumberFields.get(entity)?.push({ prop, options })
Property({
type: "number",
columnType: "numeric",
fieldName: columnName,
serializer: () => {
return undefined
},
...options,
})(target, targetColumn)
if (!entity.prototype.__bigNumberInitialized) {
entity.prototype.__bigNumberInitialized = true
registerGlobalHook(entity)
}
}
}
function registerGlobalHook(entity: any) {
const originalOnInit = entity.prototype.onInit
const originalOnCreate = entity.prototype.onCreate
const originalOnUpdate = entity.prototype.onUpdate
entity.prototype.onInit = function (...args: any[]) {
initializeBigNumberFields(this)
if (originalOnInit) {
originalOnInit.apply(this, args)
}
}
entity.prototype.onCreate = function (...args: any[]) {
initializeBigNumberFields(this)
if (originalOnCreate) {
originalOnCreate.apply(this, args)
}
}
entity.prototype.onUpdate = function (...args: any[]) {
initializeBigNumberFields(this)
if (originalOnUpdate) {
originalOnUpdate.apply(this, args)
}
}
}
function initializeBigNumberFields(entity: any) {
const fields = bigNumberFields.get(entity.constructor) ?? []
for (const field of fields) {
const { prop, options } = field
const rawValue = entity[`raw_${prop}`]
const value = entity[prop]
if (options.nullable && rawValue === null && value === null) {
return
}
const val = new BigNumber(rawValue ?? value)
entity[prop] = val.numeric
entity[`raw_${prop}`] = val.raw
Property({
type: "number",
persist: false,
getter: true,
setter: true,
})(target, columnName)
}
}