feat(utils): Fix big number decorator and cleanup (#6473)
**What** - Fix big number decorator and cleanup
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user