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
+1 -1
View File
@@ -3,7 +3,7 @@ module.exports = {
"^.+\\.[jt]s?$": [
"ts-jest",
{
tsConfig: "tsconfig.json",
tsConfig: "tsconfig.spec.json",
isolatedModules: true,
},
],
@@ -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)
}
}
+15 -6
View File
@@ -8,11 +8,16 @@ export class BigNumber {
private numeric_: number
private raw_?: BigNumberRawValue
constructor(rawPrice: BigNumberInput) {
this.setRawPriceOrThrow(rawPrice)
constructor(rawPrice: BigNumberInput, options?: { precision?: number }) {
this.setRawPriceOrThrow(rawPrice, options)
}
setRawPriceOrThrow(rawPrice: BigNumberInput) {
setRawPriceOrThrow(
rawPrice: BigNumberInput,
{ precision }: { precision?: number } = {}
) {
precision ??= BigNumber.DEFAULT_PRECISION
if (BigNumberJS.isBigNumber(rawPrice)) {
/**
* Example:
@@ -21,7 +26,8 @@ export class BigNumber {
*/
this.numeric_ = rawPrice.toNumber()
this.raw_ = {
value: rawPrice.toPrecision(BigNumber.DEFAULT_PRECISION),
value: rawPrice.toPrecision(precision),
precision,
}
} else if (isString(rawPrice)) {
/**
@@ -31,7 +37,8 @@ export class BigNumber {
this.numeric_ = bigNum.toNumber()
this.raw_ = this.raw_ = {
value: bigNum.toPrecision(BigNumber.DEFAULT_PRECISION),
value: bigNum.toPrecision(precision),
precision,
}
} else if (isBigNumber(rawPrice)) {
/**
@@ -41,6 +48,7 @@ export class BigNumber {
this.raw_ = {
...rawPrice,
precision,
}
} else if (typeof rawPrice === `number` && !Number.isNaN(rawPrice)) {
/**
@@ -49,7 +57,8 @@ export class BigNumber {
this.numeric_ = rawPrice as number
this.raw_ = {
value: BigNumberJS(rawPrice as number).toString(),
value: BigNumberJS(rawPrice as number).toPrecision(precision),
precision,
}
} else {
throw new Error(
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": ["src"],
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"sourceMap": true
}
}