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

**What**
- Fix big number decorator and cleanup
This commit is contained in:
Adrien de Peretti authored and GitHub committed 2024-02-22 16:58:41 +00:00
1 parent 598ee6f49c
commit 36a61658f9
14 files changed
+757 -773

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/utils": patch
---
feat(utils): Fix big number decorator and cleanup
+2 -3
View File
@@ -1,5 +1,5 @@
import { BigNumberRawValue, DAL } from "@medusajs/types" import { BigNumberRawValue, DAL } from "@medusajs/types"
import { BigNumber, BigNumberField } from "@medusajs/utils" import { BigNumber, MikroOrmBigNumberProperty } from "@medusajs/utils"
import { OptionalProps, PrimaryKey, Property } from "@mikro-orm/core" import { OptionalProps, PrimaryKey, Property } from "@mikro-orm/core"
type OptionalAdjustmentLineProps = DAL.EntityDateColumns type OptionalAdjustmentLineProps = DAL.EntityDateColumns
@@ -26,8 +26,7 @@ export default abstract class AdjustmentLine {
@Property({ columnType: "text", nullable: true }) @Property({ columnType: "text", nullable: true })
code: string | null = null code: string | null = null
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
amount: BigNumber | number amount: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
+8 -6
View File
@@ -1,9 +1,9 @@
import { BigNumberRawValue, DAL } from "@medusajs/types" import { BigNumberRawValue, DAL } from "@medusajs/types"
import { import {
BigNumber, BigNumber,
BigNumberField,
createPsqlIndexStatementHelper, createPsqlIndexStatementHelper,
generateEntityId, generateEntityId,
MikroOrmBigNumberProperty,
} from "@medusajs/utils" } from "@medusajs/utils"
import { import {
BeforeCreate, BeforeCreate,
@@ -11,8 +11,8 @@ import {
Collection, Collection,
Entity, Entity,
ManyToOne, ManyToOne,
OnInit,
OneToMany, OneToMany,
OnInit,
OptionalProps, OptionalProps,
PrimaryKey, PrimaryKey,
Property, Property,
@@ -114,15 +114,17 @@ export default class LineItem {
@Property({ columnType: "boolean" }) @Property({ columnType: "boolean" })
is_tax_inclusive = false is_tax_inclusive = false
@Property({ columnType: "numeric", nullable: true }) @MikroOrmBigNumberProperty({
@BigNumberField({ nullable: true }) nullable: true,
})
compare_at_unit_price?: BigNumber | number | null = null compare_at_unit_price?: BigNumber | number | null = null
@Property({ columnType: "jsonb", nullable: true }) @Property({ columnType: "jsonb", nullable: true })
raw_compare_at_unit_price: BigNumberRawValue | null = null raw_compare_at_unit_price: BigNumberRawValue | null = null
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty({
@BigNumberField() nullable: true,
})
unit_price: BigNumber | number unit_price: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
+8 -15
View File
@@ -1,9 +1,9 @@
import { BigNumberRawValue, DAL } from "@medusajs/types" import { BigNumberRawValue, DAL } from "@medusajs/types"
import { import {
BigNumber, BigNumber,
BigNumberField,
createPsqlIndexStatementHelper, createPsqlIndexStatementHelper,
generateEntityId, generateEntityId,
MikroOrmBigNumberProperty,
} from "@medusajs/utils" } from "@medusajs/utils"
import { import {
BeforeCreate, BeforeCreate,
@@ -57,50 +57,43 @@ export default class OrderDetail {
}) })
item: LineItem item: LineItem
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
quantity: BigNumber | number quantity: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
raw_quantity: BigNumberRawValue raw_quantity: BigNumberRawValue
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
fulfilled_quantity: BigNumber | number fulfilled_quantity: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
raw_fulfilled_quantity: BigNumberRawValue raw_fulfilled_quantity: BigNumberRawValue
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
shipped_quantity: BigNumber | number shipped_quantity: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
raw_shipped_quantity: BigNumberRawValue raw_shipped_quantity: BigNumberRawValue
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
return_requested_quantity: BigNumber | number return_requested_quantity: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
raw_return_requested_quantity: BigNumberRawValue raw_return_requested_quantity: BigNumberRawValue
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
return_received_quantity: BigNumber | number return_received_quantity: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
raw_return_received_quantity: BigNumberRawValue raw_return_received_quantity: BigNumberRawValue
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
return_dismissed_quantity: BigNumber | number return_dismissed_quantity: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
raw_return_dismissed_quantity: BigNumberRawValue raw_return_dismissed_quantity: BigNumberRawValue
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
written_off_quantity: BigNumber | number written_off_quantity: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
+3 -4
View File
@@ -1,9 +1,9 @@
import { BigNumberRawValue } from "@medusajs/types" import { BigNumberRawValue } from "@medusajs/types"
import { import {
BigNumber, BigNumber,
BigNumberField,
createPsqlIndexStatementHelper, createPsqlIndexStatementHelper,
generateEntityId, generateEntityId,
MikroOrmBigNumberProperty,
} from "@medusajs/utils" } from "@medusajs/utils"
import { import {
BeforeCreate, BeforeCreate,
@@ -12,8 +12,8 @@ import {
Collection, Collection,
Entity, Entity,
ManyToOne, ManyToOne,
OnInit,
OneToMany, OneToMany,
OnInit,
PrimaryKey, PrimaryKey,
Property, Property,
} from "@mikro-orm/core" } from "@mikro-orm/core"
@@ -54,8 +54,7 @@ export default class ShippingMethod {
@Property({ columnType: "jsonb", nullable: true }) @Property({ columnType: "jsonb", nullable: true })
description: string | null = null description: string | null = null
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
amount: BigNumber | number amount: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
+2 -3
View File
@@ -1,5 +1,5 @@
import { BigNumberRawValue } from "@medusajs/types" import { BigNumberRawValue } from "@medusajs/types"
import { BigNumber, BigNumberField } from "@medusajs/utils" import {BigNumber, MikroOrmBigNumberProperty} from "@medusajs/utils"
import { PrimaryKey, Property } from "@mikro-orm/core" import { PrimaryKey, Property } from "@mikro-orm/core"
/** /**
@@ -22,8 +22,7 @@ export default abstract class TaxLine {
@Property({ columnType: "text" }) @Property({ columnType: "text" })
code: string code: string
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
rate: BigNumber | number rate: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
+2 -3
View File
@@ -1,9 +1,9 @@
import { BigNumberRawValue, DAL } from "@medusajs/types" import { BigNumberRawValue, DAL } from "@medusajs/types"
import { import {
BigNumber, BigNumber,
BigNumberField,
createPsqlIndexStatementHelper, createPsqlIndexStatementHelper,
generateEntityId, generateEntityId,
MikroOrmBigNumberProperty,
} from "@medusajs/utils" } from "@medusajs/utils"
import { import {
BeforeCreate, BeforeCreate,
@@ -52,8 +52,7 @@ export default class Transaction {
}) })
order: Order order: Order
@Property({ columnType: "numeric" }) @MikroOrmBigNumberProperty()
@BigNumberField()
amount: BigNumber | number amount: BigNumber | number
@Property({ columnType: "jsonb" }) @Property({ columnType: "jsonb" })
File diff suppressed because it is too large. Load diff
+12 -4
View File
@@ -9,7 +9,13 @@ import {
PrimaryKey, PrimaryKey,
Property, Property,
} from "@mikro-orm/core" } from "@mikro-orm/core"
import { generateEntityId, PaymentSessionStatus } from "@medusajs/utils" import {
BigNumber,
generateEntityId,
MikroOrmBigNumberProperty,
PaymentSessionStatus,
} from "@medusajs/utils"
import { BigNumberRawValue } from "@medusajs/types"
import PaymentCollection from "./payment-collection" import PaymentCollection from "./payment-collection"
import Payment from "./payment" import Payment from "./payment"
@@ -24,11 +30,13 @@ export default class PaymentSession {
@Property({ columnType: "text" }) @Property({ columnType: "text" })
currency_code: string currency_code: string
@MikroOrmBigNumberProperty()
amount: BigNumber | number
@Property({ @Property({
columnType: "numeric", columnType: "jsonb",
serializer: Number,
}) })
amount: number raw_amount: BigNumberRawValue
@Property({ columnType: "text" }) @Property({ columnType: "text" })
provider_id: string provider_id: string
+1 -1
View File
@@ -3,7 +3,7 @@ module.exports = {
"^.+\\.[jt]s?$": [ "^.+\\.[jt]s?$": [
"ts-jest", "ts-jest",
{ {
tsConfig: "tsconfig.json", tsConfig: "tsconfig.spec.json",
isolatedModules: true, 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 { BigNumber } from "../../totals/big-number"
import { Property } from "@mikro-orm/core"
import { BigNumberInput } from "@medusajs/types"
const bigNumberFields = new WeakMap< export function MikroOrmBigNumberProperty(
object, options: Parameters<typeof Property>[0] & {
{ prop: string; options: { nullable?: boolean } }[] rawColumnName?: string
>() } = {}
) {
return function (target: any, columnName: string) {
const targetColumn = columnName + "_"
const rawColumnName = options.rawColumnName ?? `raw_${columnName}`
export function BigNumberField(options: { nullable?: boolean } = {}) { Object.defineProperty(target, columnName, {
return function (target: any, prop: string) { get() {
const entity = target.constructor return this[targetColumn]
if (!bigNumberFields.has(entity)) { },
bigNumberFields.set(entity, []) 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_")) { this[targetColumn] = bigNumber.numeric
const suggestedPropName = prop.replace("raw_", "") this[rawColumnName] = bigNumber.raw
throw new Error( },
`BigNumberField decorator has to be used on the property "${suggestedPropName}" and ${prop} typed as BigNumberRawValue.` })
)
}
bigNumberFields.get(entity)?.push({ prop, options }) Property({
type: "number",
columnType: "numeric",
fieldName: columnName,
serializer: () => {
return undefined
},
...options,
})(target, targetColumn)
if (!entity.prototype.__bigNumberInitialized) { Property({
entity.prototype.__bigNumberInitialized = true type: "number",
persist: false,
registerGlobalHook(entity) getter: true,
} setter: true,
} })(target, columnName)
}
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
} }
} }
+15 -6
View File
@@ -8,11 +8,16 @@ export class BigNumber {
private numeric_: number private numeric_: number
private raw_?: BigNumberRawValue private raw_?: BigNumberRawValue
constructor(rawPrice: BigNumberInput) { constructor(rawPrice: BigNumberInput, options?: { precision?: number }) {
this.setRawPriceOrThrow(rawPrice) this.setRawPriceOrThrow(rawPrice, options)
} }
setRawPriceOrThrow(rawPrice: BigNumberInput) { setRawPriceOrThrow(
rawPrice: BigNumberInput,
{ precision }: { precision?: number } = {}
) {
precision ??= BigNumber.DEFAULT_PRECISION
if (BigNumberJS.isBigNumber(rawPrice)) { if (BigNumberJS.isBigNumber(rawPrice)) {
/** /**
* Example: * Example:
@@ -21,7 +26,8 @@ export class BigNumber {
*/ */
this.numeric_ = rawPrice.toNumber() this.numeric_ = rawPrice.toNumber()
this.raw_ = { this.raw_ = {
value: rawPrice.toPrecision(BigNumber.DEFAULT_PRECISION), value: rawPrice.toPrecision(precision),
precision,
} }
} else if (isString(rawPrice)) { } else if (isString(rawPrice)) {
/** /**
@@ -31,7 +37,8 @@ export class BigNumber {
this.numeric_ = bigNum.toNumber() this.numeric_ = bigNum.toNumber()
this.raw_ = this.raw_ = { this.raw_ = this.raw_ = {
value: bigNum.toPrecision(BigNumber.DEFAULT_PRECISION), value: bigNum.toPrecision(precision),
precision,
} }
} else if (isBigNumber(rawPrice)) { } else if (isBigNumber(rawPrice)) {
/** /**
@@ -41,6 +48,7 @@ export class BigNumber {
this.raw_ = { this.raw_ = {
...rawPrice, ...rawPrice,
precision,
} }
} else if (typeof rawPrice === `number` && !Number.isNaN(rawPrice)) { } else if (typeof rawPrice === `number` && !Number.isNaN(rawPrice)) {
/** /**
@@ -49,7 +57,8 @@ export class BigNumber {
this.numeric_ = rawPrice as number this.numeric_ = rawPrice as number
this.raw_ = { this.raw_ = {
value: BigNumberJS(rawPrice as number).toString(), value: BigNumberJS(rawPrice as number).toPrecision(precision),
precision,
} }
} else { } else {
throw new Error( throw new Error(
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": ["src"],
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"sourceMap": true
}
}