Skip to content

Commit

Permalink
do string to float casting with some safety nets
Browse files Browse the repository at this point in the history
  • Loading branch information
hotzevzl committed May 9, 2024
1 parent 2b67dd2 commit 481218f
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { numericStringToFloat } from '@marxan/utils/numeric-string-to-float.utils';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { Column, ViewEntity } from 'typeorm';
Expand All @@ -24,7 +25,7 @@ export class ScenarioFeaturesGapData {
// explicitly set type, otherwise TypeORM (v10, at least) will cast to integer
// TypeORM will still represent the value as string though (https://github.com/typeorm/typeorm/issues/873#issuecomment-328912050)
@Column({ name: 'met', type: 'double precision' })
@Transform(parseFloat)
@Transform(numericStringToFloat)
met!: number;

@ApiProperty()
Expand All @@ -35,7 +36,7 @@ export class ScenarioFeaturesGapData {
// explicitly set type, otherwise TypeORM (v10, at least) will cast to integer
// TypeORM will still represent the value as string though (https://github.com/typeorm/typeorm/issues/873#issuecomment-328912050)
@Column({ name: 'coverage_target', type: 'double precision' })
@Transform(parseFloat)
@Transform(numericStringToFloat)
coverageTarget!: number;

@ApiProperty()
Expand Down
21 changes: 21 additions & 0 deletions api/libs/utils/src/numeric-string-to-float.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { numericStringToFloat } from './numeric-string-to-float.utils';

describe('parseOptionalFloat', () => {
it('should return undefined if the value is undefined', () => {
expect(numericStringToFloat(undefined)).toBeUndefined();
});

it('should throw an exception if the value is not numeric', () => {
expect(() => numericStringToFloat('foo')).toThrow('Invalid number: foo');
});

it('should return a numeric representation of the value if the value is numeric', () => {
expect(numericStringToFloat('123.456')).toBe(123.456);
});

it('should silently round a float to the maximum precision supported by javascript', () => {
expect(numericStringToFloat('123.456789012345678901234567890')).toBe(
123.45678901234568,
);
});
});
24 changes: 24 additions & 0 deletions api/libs/utils/src/numeric-string-to-float.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { isNil } from 'lodash';

/**
* Kind of like parseFloat(), but passing through undefined values, and handling
* values that don't cast to a number.
*
* @debt It silently rounds to the maximum precision supported by Javascript any
* input values that are numeric but beyond what can be represented in a
* Javascript number (not BigInt). Infinity and -Infinity are also passed
* through as corresponding Javascript Infinity numeric values.
*/
export function numericStringToFloat(
value: string | undefined,
): number | undefined {
// +(null) === 0, so we only cast if input is neither undefined nor null.
if (!isNil(value)) {
const floatValue = +value;
if (!isNaN(floatValue)) {
return floatValue;
}
throw new Error(`Invalid number: ${value}`);
}
return;
}

0 comments on commit 481218f

Please sign in to comment.