mirror of
https://github.com/unraid/api.git
synced 2026-01-01 06:01:18 -06:00
feat: use bigint instead of long (#1403)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved support for large integer values in the API, allowing accurate handling of big numbers in GraphQL queries and responses. - **Bug Fixes** - Enhanced reliability when dealing with very large numeric fields, reducing the risk of data loss or inaccuracies for disk, share, and memory statistics. - **Chores** - Updated internal dependencies to improve handling of big integers. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -115,6 +115,7 @@
|
|||||||
"ini": "^5.0.0",
|
"ini": "^5.0.0",
|
||||||
"ip": "^2.0.1",
|
"ip": "^2.0.1",
|
||||||
"jose": "^6.0.0",
|
"jose": "^6.0.0",
|
||||||
|
"json-bigint-patch": "^0.0.8",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"multi-ini": "^2.3.2",
|
"multi-ini": "^2.3.2",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import '@app/dotenv.js';
|
import '@app/dotenv.js';
|
||||||
|
import 'json-bigint-patch';
|
||||||
|
|
||||||
import { execa } from 'execa';
|
import { execa } from 'execa';
|
||||||
import { CommandFactory } from 'nest-commander';
|
import { CommandFactory } from 'nest-commander';
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import 'global-agent/bootstrap.js';
|
import 'global-agent/bootstrap.js';
|
||||||
|
import 'json-bigint-patch';
|
||||||
import '@app/dotenv.js';
|
import '@app/dotenv.js';
|
||||||
|
|
||||||
import { type NestFastifyApplication } from '@nestjs/platform-fastify';
|
import { type NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Module } from '@nestjs/common';
|
|||||||
import { GraphQLModule } from '@nestjs/graphql';
|
import { GraphQLModule } from '@nestjs/graphql';
|
||||||
|
|
||||||
import { NoUnusedVariablesRule } from 'graphql';
|
import { NoUnusedVariablesRule } from 'graphql';
|
||||||
import { JSONResolver, URLResolver } from 'graphql-scalars';
|
import { GraphQLBigInt, JSONResolver, URLResolver } from 'graphql-scalars';
|
||||||
|
|
||||||
import { ENVIRONMENT } from '@app/environment.js';
|
import { ENVIRONMENT } from '@app/environment.js';
|
||||||
import { getters } from '@app/store/index.js';
|
import { getters } from '@app/store/index.js';
|
||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
} from '@app/unraid-api/graph/directives/use-permissions.directive.js';
|
} from '@app/unraid-api/graph/directives/use-permissions.directive.js';
|
||||||
import { ResolversModule } from '@app/unraid-api/graph/resolvers/resolvers.module.js';
|
import { ResolversModule } from '@app/unraid-api/graph/resolvers/resolvers.module.js';
|
||||||
import { sandboxPlugin } from '@app/unraid-api/graph/sandbox-plugin.js';
|
import { sandboxPlugin } from '@app/unraid-api/graph/sandbox-plugin.js';
|
||||||
import { GraphQLLong } from '@app/unraid-api/graph/scalars/graphql-type-long.js';
|
|
||||||
import { PrefixedID as PrefixedIDScalar } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
import { PrefixedID as PrefixedIDScalar } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||||
import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js';
|
import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js';
|
||||||
|
|
||||||
@@ -50,7 +49,7 @@ import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js';
|
|||||||
},
|
},
|
||||||
resolvers: {
|
resolvers: {
|
||||||
JSON: JSONResolver,
|
JSON: JSONResolver,
|
||||||
Long: GraphQLLong,
|
Long: GraphQLBigInt,
|
||||||
URL: URLResolver,
|
URL: URLResolver,
|
||||||
},
|
},
|
||||||
buildSchemaOptions: {
|
buildSchemaOptions: {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Field, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
|
import { Field, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||||
|
|
||||||
import { IsEnum } from 'class-validator';
|
import { IsEnum } from 'class-validator';
|
||||||
|
import { GraphQLBigInt } from 'graphql-scalars';
|
||||||
|
|
||||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||||
import { GraphQLLong } from '@app/unraid-api/graph/scalars/graphql-type-long.js';
|
|
||||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
@@ -43,7 +43,7 @@ export class ArrayDisk extends Node {
|
|||||||
@Field(() => String, { nullable: true })
|
@Field(() => String, { nullable: true })
|
||||||
device?: string;
|
device?: string;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, { description: '(KB) Disk Size total', nullable: true })
|
@Field(() => GraphQLBigInt, { description: '(KB) Disk Size total', nullable: true })
|
||||||
size?: number | null;
|
size?: number | null;
|
||||||
|
|
||||||
@Field(() => ArrayDiskStatus, { nullable: true })
|
@Field(() => ArrayDiskStatus, { nullable: true })
|
||||||
@@ -58,40 +58,40 @@ export class ArrayDisk extends Node {
|
|||||||
})
|
})
|
||||||
temp?: number | null;
|
temp?: number | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, {
|
@Field(() => GraphQLBigInt, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description:
|
description:
|
||||||
'Count of I/O read requests sent to the device I/O drivers. These statistics may be cleared at any time.',
|
'Count of I/O read requests sent to the device I/O drivers. These statistics may be cleared at any time.',
|
||||||
})
|
})
|
||||||
numReads?: number | null;
|
numReads?: number | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, {
|
@Field(() => GraphQLBigInt, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description:
|
description:
|
||||||
'Count of I/O writes requests sent to the device I/O drivers. These statistics may be cleared at any time.',
|
'Count of I/O writes requests sent to the device I/O drivers. These statistics may be cleared at any time.',
|
||||||
})
|
})
|
||||||
numWrites?: number | null;
|
numWrites?: number | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, {
|
@Field(() => GraphQLBigInt, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description:
|
description:
|
||||||
'Number of unrecoverable errors reported by the device I/O drivers. Missing data due to unrecoverable array read errors is filled in on-the-fly using parity reconstruct (and we attempt to write this data back to the sector(s) which failed). Any unrecoverable write error results in disabling the disk.',
|
'Number of unrecoverable errors reported by the device I/O drivers. Missing data due to unrecoverable array read errors is filled in on-the-fly using parity reconstruct (and we attempt to write this data back to the sector(s) which failed). Any unrecoverable write error results in disabling the disk.',
|
||||||
})
|
})
|
||||||
numErrors?: number | null;
|
numErrors?: number | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, {
|
@Field(() => GraphQLBigInt, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: '(KB) Total Size of the FS (Not present on Parity type drive)',
|
description: '(KB) Total Size of the FS (Not present on Parity type drive)',
|
||||||
})
|
})
|
||||||
fsSize?: number | null;
|
fsSize?: number | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, {
|
@Field(() => GraphQLBigInt, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: '(KB) Free Size on the FS (Not present on Parity type drive)',
|
description: '(KB) Free Size on the FS (Not present on Parity type drive)',
|
||||||
})
|
})
|
||||||
fsFree?: number | null;
|
fsFree?: number | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, {
|
@Field(() => GraphQLBigInt, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: '(KB) Used Size on the FS (Not present on Parity type drive)',
|
description: '(KB) Used Size on the FS (Not present on Parity type drive)',
|
||||||
})
|
})
|
||||||
@@ -243,13 +243,13 @@ export class Share extends Node {
|
|||||||
@Field(() => String, { description: 'Display name', nullable: true })
|
@Field(() => String, { description: 'Display name', nullable: true })
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, { description: '(KB) Free space', nullable: true })
|
@Field(() => GraphQLBigInt, { description: '(KB) Free space', nullable: true })
|
||||||
free?: number | null;
|
free?: number | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, { description: '(KB) Used Size', nullable: true })
|
@Field(() => GraphQLBigInt, { description: '(KB) Used Size', nullable: true })
|
||||||
used?: number | null;
|
used?: number | null;
|
||||||
|
|
||||||
@Field(() => GraphQLLong, { description: '(KB) Total size', nullable: true })
|
@Field(() => GraphQLBigInt, { description: '(KB) Total size', nullable: true })
|
||||||
size?: number | null;
|
size?: number | null;
|
||||||
|
|
||||||
@Field(() => [String], { description: 'Disks that are included in this share', nullable: true })
|
@Field(() => [String], { description: 'Disks that are included in this share', nullable: true })
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
registerEnumType,
|
registerEnumType,
|
||||||
} from '@nestjs/graphql';
|
} from '@nestjs/graphql';
|
||||||
|
|
||||||
import { GraphQLJSON } from 'graphql-scalars';
|
import { GraphQLBigInt, GraphQLJSON } from 'graphql-scalars';
|
||||||
|
|
||||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||||
import { ThemeName } from '@app/unraid-api/graph/resolvers/customization/theme.model.js';
|
import { ThemeName } from '@app/unraid-api/graph/resolvers/customization/theme.model.js';
|
||||||
@@ -290,7 +290,7 @@ export class Display extends Node {
|
|||||||
|
|
||||||
@ObjectType({ implements: () => Node })
|
@ObjectType({ implements: () => Node })
|
||||||
export class MemoryLayout extends Node {
|
export class MemoryLayout extends Node {
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
size!: number;
|
size!: number;
|
||||||
|
|
||||||
@Field(() => String, { nullable: true })
|
@Field(() => String, { nullable: true })
|
||||||
@@ -326,34 +326,34 @@ export class MemoryLayout extends Node {
|
|||||||
|
|
||||||
@ObjectType({ implements: () => Node })
|
@ObjectType({ implements: () => Node })
|
||||||
export class InfoMemory extends Node {
|
export class InfoMemory extends Node {
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
max!: number;
|
max!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
total!: number;
|
total!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
free!: number;
|
free!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
used!: number;
|
used!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
active!: number;
|
active!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
available!: number;
|
available!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
buffcache!: number;
|
buffcache!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
swaptotal!: number;
|
swaptotal!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
swapused!: number;
|
swapused!: number;
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => GraphQLBigInt)
|
||||||
swapfree!: number;
|
swapfree!: number;
|
||||||
|
|
||||||
@Field(() => [MemoryLayout])
|
@Field(() => [MemoryLayout])
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
import type { ASTNode } from 'graphql';
|
|
||||||
import { GraphQLScalarType } from 'graphql';
|
|
||||||
import { Kind } from 'graphql/language/index.js';
|
|
||||||
|
|
||||||
const MAX_LONG = Number.MAX_SAFE_INTEGER;
|
|
||||||
const MIN_LONG = Number.MIN_SAFE_INTEGER;
|
|
||||||
|
|
||||||
const coerceLong = (value) => {
|
|
||||||
if (value === '')
|
|
||||||
throw new TypeError('Long cannot represent non 52-bit signed integer value: (empty string)');
|
|
||||||
const num = Number(value);
|
|
||||||
if (num == num && num <= MAX_LONG && num >= MIN_LONG) {
|
|
||||||
if (num < 0) {
|
|
||||||
return Math.ceil(num);
|
|
||||||
}
|
|
||||||
return Math.floor(num);
|
|
||||||
}
|
|
||||||
throw new TypeError('Long cannot represent non 52-bit signed integer value: ' + String(value));
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseLiteral = (ast: ASTNode) => {
|
|
||||||
if (ast.kind === Kind.INT) {
|
|
||||||
const num = parseInt(ast.value, 10);
|
|
||||||
if (num <= MAX_LONG && num >= MIN_LONG) return num;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GraphQLLong = new GraphQLScalarType({
|
|
||||||
name: 'Long',
|
|
||||||
description: 'The `Long` scalar type represents 52-bit integers',
|
|
||||||
serialize: coerceLong,
|
|
||||||
parseValue: coerceLong,
|
|
||||||
parseLiteral: parseLiteral,
|
|
||||||
});
|
|
||||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -222,6 +222,9 @@ importers:
|
|||||||
jose:
|
jose:
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.10
|
version: 6.0.10
|
||||||
|
json-bigint-patch:
|
||||||
|
specifier: ^0.0.8
|
||||||
|
version: 0.0.8
|
||||||
lodash-es:
|
lodash-es:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@@ -8285,6 +8288,9 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
json-bigint-patch@0.0.8:
|
||||||
|
resolution: {integrity: sha512-xa0LTQsyaq8awYyZyuUsporWisZFiyqzxGW8CKM3t7oouf0GFAKYJnqAm6e9NLNBQOCtOLvy614DEiRX/rPbnA==}
|
||||||
|
|
||||||
json-buffer@3.0.1:
|
json-buffer@3.0.1:
|
||||||
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
||||||
|
|
||||||
@@ -20855,6 +20861,8 @@ snapshots:
|
|||||||
|
|
||||||
jsesc@3.1.0: {}
|
jsesc@3.1.0: {}
|
||||||
|
|
||||||
|
json-bigint-patch@0.0.8: {}
|
||||||
|
|
||||||
json-buffer@3.0.1: {}
|
json-buffer@3.0.1: {}
|
||||||
|
|
||||||
json-parse-better-errors@1.0.2: {}
|
json-parse-better-errors@1.0.2: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user