@oneuptime/common 11.7.2 → 11.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
- package/Models/DatabaseModels/TableView.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
- package/Tests/Types/JSONFunctions.test.ts +103 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +572 -0
- package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
- package/Tests/Utils/UserPreferences.test.ts +563 -0
- package/Types/JSONFunctions.ts +7 -2
- package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
- package/UI/Components/MasterPage/MasterPage.tsx +65 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +387 -4
- package/UI/Components/ModelTable/Column.ts +17 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +420 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
- package/UI/Components/ModelTable/TableView.tsx +24 -2
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
- package/UI/Components/SideMenu/SideMenu.tsx +24 -4
- package/UI/Components/Table/Table.tsx +14 -1
- package/UI/Components/Table/TableRow.tsx +180 -175
- package/UI/Components/Table/Types/Column.ts +2 -0
- package/Utils/UserPreferences.ts +53 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TableView.js +40 -0
- package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +8 -3
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +239 -7
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +189 -0
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +15 -2
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +11 -6
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/Utils/UserPreferences.js +33 -0
- package/build/dist/Utils/UserPreferences.js.map +1 -1
- package/package.json +1 -1
|
@@ -348,6 +348,12 @@ export default class KubernetesCostAllocation extends AnalyticsBaseModel {
|
|
|
348
348
|
title: "CPU Core Usage Average",
|
|
349
349
|
description: "Average CPU cores actually used over the window.",
|
|
350
350
|
},
|
|
351
|
+
{
|
|
352
|
+
key: "cpuCoreLimitAverage",
|
|
353
|
+
title: "CPU Core Limit Average",
|
|
354
|
+
description:
|
|
355
|
+
"Average CPU core limit over the window. 0 when no limit is set — which is also what a row ingested before this column reads back.",
|
|
356
|
+
},
|
|
351
357
|
{
|
|
352
358
|
key: "cpuCost",
|
|
353
359
|
title: "CPU Cost",
|
|
@@ -378,6 +384,18 @@ export default class KubernetesCostAllocation extends AnalyticsBaseModel {
|
|
|
378
384
|
title: "RAM Bytes Usage Average",
|
|
379
385
|
description: "Average RAM bytes actually used over the window.",
|
|
380
386
|
},
|
|
387
|
+
{
|
|
388
|
+
key: "ramBytesLimitAverage",
|
|
389
|
+
title: "RAM Bytes Limit Average",
|
|
390
|
+
description:
|
|
391
|
+
"Average RAM byte limit over the window. 0 when no limit is set — which is also what a row ingested before this column reads back.",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
key: "ramBytesUsageMax",
|
|
395
|
+
title: "RAM Bytes Usage Max",
|
|
396
|
+
description:
|
|
397
|
+
"Peak RAM bytes used during the window, from Prometheus rather than the cost engine — the engine only reports averages, and an hourly mean hides the burst that OOMKills a container. 0 when the agent has no Prometheus configured, when the scrape had no data, or on rows ingested before this column; treat 0 as 'unknown', never as a real peak.",
|
|
398
|
+
},
|
|
381
399
|
{
|
|
382
400
|
key: "ramCost",
|
|
383
401
|
title: "RAM Cost",
|
|
@@ -472,6 +490,49 @@ export default class KubernetesCostAllocation extends AnalyticsBaseModel {
|
|
|
472
490
|
},
|
|
473
491
|
});
|
|
474
492
|
|
|
493
|
+
/*
|
|
494
|
+
* Delivery bookkeeping, not workload data. A window wider than the
|
|
495
|
+
* agent's batch size arrives as several independent ingest jobs, so the
|
|
496
|
+
* ingest service needs to tell "another chunk of the delivery I am
|
|
497
|
+
* already ingesting" (accept) from "a window a previous delivery already
|
|
498
|
+
* ingested" (drop, or a restarted agent double-counts spend). shipmentId
|
|
499
|
+
* is the agent's content hash of the window — identical across restarts,
|
|
500
|
+
* different when the engine's answer for that window changed.
|
|
501
|
+
*
|
|
502
|
+
* Empty / 0 on rows written before these columns existed and on rows
|
|
503
|
+
* from agents older than the shipment contract; the ingest service falls
|
|
504
|
+
* back to the whole-window guard for those.
|
|
505
|
+
*/
|
|
506
|
+
const shipmentIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
|
507
|
+
key: "shipmentId",
|
|
508
|
+
title: "Shipment ID",
|
|
509
|
+
description:
|
|
510
|
+
"Content hash identifying the agent delivery this row arrived in. Used to deduplicate re-shipped windows; empty for rows ingested before the shipment contract.",
|
|
511
|
+
required: true,
|
|
512
|
+
defaultValue: "",
|
|
513
|
+
type: TableColumnType.Text,
|
|
514
|
+
accessControl: {
|
|
515
|
+
read: readPermissions,
|
|
516
|
+
create: createPermissions,
|
|
517
|
+
update: [],
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const shipmentChunkColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
|
522
|
+
key: "shipmentChunk",
|
|
523
|
+
title: "Shipment Chunk",
|
|
524
|
+
description:
|
|
525
|
+
"Index of the request within its shipment that carried this row. 0 for single-request shipments and for rows ingested before the shipment contract.",
|
|
526
|
+
required: true,
|
|
527
|
+
defaultValue: 0,
|
|
528
|
+
type: TableColumnType.Number,
|
|
529
|
+
accessControl: {
|
|
530
|
+
read: readPermissions,
|
|
531
|
+
create: createPermissions,
|
|
532
|
+
update: [],
|
|
533
|
+
},
|
|
534
|
+
});
|
|
535
|
+
|
|
475
536
|
const retentionDateColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
|
476
537
|
key: "retentionDate",
|
|
477
538
|
codec: [{ codec: "DoubleDelta" }, { codec: "ZSTD", level: 1 }],
|
|
@@ -517,6 +578,8 @@ export default class KubernetesCostAllocation extends AnalyticsBaseModel {
|
|
|
517
578
|
labelKeysColumn,
|
|
518
579
|
...measureColumns,
|
|
519
580
|
currencyColumn,
|
|
581
|
+
shipmentIdColumn,
|
|
582
|
+
shipmentChunkColumn,
|
|
520
583
|
retentionDateColumn,
|
|
521
584
|
],
|
|
522
585
|
sortKeys: [
|
|
@@ -688,6 +751,14 @@ export default class KubernetesCostAllocation extends AnalyticsBaseModel {
|
|
|
688
751
|
this.setColumnValue("cpuCoreUsageAverage", v);
|
|
689
752
|
}
|
|
690
753
|
|
|
754
|
+
public get cpuCoreLimitAverage(): number | undefined {
|
|
755
|
+
return this.getColumnValue("cpuCoreLimitAverage") as number | undefined;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
public set cpuCoreLimitAverage(v: number | undefined) {
|
|
759
|
+
this.setColumnValue("cpuCoreLimitAverage", v);
|
|
760
|
+
}
|
|
761
|
+
|
|
691
762
|
public get cpuCost(): number | undefined {
|
|
692
763
|
return this.getColumnValue("cpuCost") as number | undefined;
|
|
693
764
|
}
|
|
@@ -736,6 +807,22 @@ export default class KubernetesCostAllocation extends AnalyticsBaseModel {
|
|
|
736
807
|
this.setColumnValue("ramBytesUsageAverage", v);
|
|
737
808
|
}
|
|
738
809
|
|
|
810
|
+
public get ramBytesLimitAverage(): number | undefined {
|
|
811
|
+
return this.getColumnValue("ramBytesLimitAverage") as number | undefined;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
public set ramBytesLimitAverage(v: number | undefined) {
|
|
815
|
+
this.setColumnValue("ramBytesLimitAverage", v);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
public get ramBytesUsageMax(): number | undefined {
|
|
819
|
+
return this.getColumnValue("ramBytesUsageMax") as number | undefined;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
public set ramBytesUsageMax(v: number | undefined) {
|
|
823
|
+
this.setColumnValue("ramBytesUsageMax", v);
|
|
824
|
+
}
|
|
825
|
+
|
|
739
826
|
public get ramCost(): number | undefined {
|
|
740
827
|
return this.getColumnValue("ramCost") as number | undefined;
|
|
741
828
|
}
|
|
@@ -832,6 +919,22 @@ export default class KubernetesCostAllocation extends AnalyticsBaseModel {
|
|
|
832
919
|
this.setColumnValue("currency", v);
|
|
833
920
|
}
|
|
834
921
|
|
|
922
|
+
public get shipmentId(): string | undefined {
|
|
923
|
+
return this.getColumnValue("shipmentId") as string | undefined;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
public set shipmentId(v: string | undefined) {
|
|
927
|
+
this.setColumnValue("shipmentId", v);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
public get shipmentChunk(): number | undefined {
|
|
931
|
+
return this.getColumnValue("shipmentChunk") as number | undefined;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
public set shipmentChunk(v: number | undefined) {
|
|
935
|
+
this.setColumnValue("shipmentChunk", v);
|
|
936
|
+
}
|
|
937
|
+
|
|
835
938
|
public get retentionDate(): Date | undefined {
|
|
836
939
|
return this.getColumnValue("retentionDate") as Date | undefined;
|
|
837
940
|
}
|
|
@@ -555,4 +555,44 @@ export default class TableView extends BaseModel {
|
|
|
555
555
|
nullable: true,
|
|
556
556
|
})
|
|
557
557
|
public facets?: JSONObject = undefined;
|
|
558
|
+
|
|
559
|
+
@ColumnAccessControl({
|
|
560
|
+
create: [
|
|
561
|
+
Permission.ProjectOwner,
|
|
562
|
+
Permission.ProjectAdmin,
|
|
563
|
+
Permission.CreateTableView,
|
|
564
|
+
],
|
|
565
|
+
read: [
|
|
566
|
+
Permission.ProjectOwner,
|
|
567
|
+
Permission.ProjectAdmin,
|
|
568
|
+
Permission.ProjectMember,
|
|
569
|
+
Permission.Viewer,
|
|
570
|
+
Permission.SettingsAdmin,
|
|
571
|
+
Permission.SettingsMember,
|
|
572
|
+
Permission.SettingsViewer,
|
|
573
|
+
Permission.ReadTableView,
|
|
574
|
+
],
|
|
575
|
+
update: [
|
|
576
|
+
Permission.ProjectOwner,
|
|
577
|
+
Permission.ProjectAdmin,
|
|
578
|
+
Permission.EditTableView,
|
|
579
|
+
],
|
|
580
|
+
})
|
|
581
|
+
@TableColumn({
|
|
582
|
+
title: "Columns",
|
|
583
|
+
required: false,
|
|
584
|
+
unique: false,
|
|
585
|
+
type: TableColumnType.JSON,
|
|
586
|
+
canReadOnRelationQuery: true,
|
|
587
|
+
description:
|
|
588
|
+
"Which columns are shown, and in what order, for this table view",
|
|
589
|
+
example:
|
|
590
|
+
'{"order": ["name", "currentMonitorStatus"], "hidden": ["labels"]}',
|
|
591
|
+
})
|
|
592
|
+
@Column({
|
|
593
|
+
type: ColumnType.JSON,
|
|
594
|
+
unique: false,
|
|
595
|
+
nullable: true,
|
|
596
|
+
})
|
|
597
|
+
public columns?: JSONObject = undefined;
|
|
558
598
|
}
|
package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddColumnsToTableView1785241000000 implements MigrationInterface {
|
|
4
|
+
public name: string = "AddColumnsToTableView1785241000000";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(`ALTER TABLE "TableView" ADD "columns" jsonb`);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
11
|
+
await queryRunner.query(`ALTER TABLE "TableView" DROP COLUMN "columns"`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -473,6 +473,7 @@ import { MigrationName1785066759532 } from "./1785066759532-MigrationName";
|
|
|
473
473
|
import { AddHotQueryIndexes1785140242697 } from "./1785140242697-AddHotQueryIndexes";
|
|
474
474
|
import { AddHotQueryIndexesSecondPass1785148065137 } from "./1785148065137-AddHotQueryIndexesSecondPass";
|
|
475
475
|
import { RepairCrossProjectMonitorStatusReferences1785240000000 } from "./1785240000000-RepairCrossProjectMonitorStatusReferences";
|
|
476
|
+
import { AddColumnsToTableView1785241000000 } from "./1785241000000-AddColumnsToTableView";
|
|
476
477
|
|
|
477
478
|
export default [
|
|
478
479
|
InitialMigration,
|
|
@@ -950,4 +951,5 @@ export default [
|
|
|
950
951
|
AddHotQueryIndexes1785140242697,
|
|
951
952
|
AddHotQueryIndexesSecondPass1785148065137,
|
|
952
953
|
RepairCrossProjectMonitorStatusReferences1785240000000,
|
|
954
|
+
AddColumnsToTableView1785241000000,
|
|
953
955
|
];
|
|
@@ -37,12 +37,15 @@ describe("Analytics KubernetesCostAllocation model", () => {
|
|
|
37
37
|
"cpuCoreHours",
|
|
38
38
|
"cpuCoreRequestAverage",
|
|
39
39
|
"cpuCoreUsageAverage",
|
|
40
|
+
"cpuCoreLimitAverage",
|
|
40
41
|
"cpuCost",
|
|
41
42
|
"gpuHours",
|
|
42
43
|
"gpuCost",
|
|
43
44
|
"ramByteHours",
|
|
44
45
|
"ramBytesRequestAverage",
|
|
45
46
|
"ramBytesUsageAverage",
|
|
47
|
+
"ramBytesLimitAverage",
|
|
48
|
+
"ramBytesUsageMax",
|
|
46
49
|
"ramCost",
|
|
47
50
|
"pvByteHours",
|
|
48
51
|
"pvCost",
|
|
@@ -95,6 +98,19 @@ describe("Analytics KubernetesCostAllocation model", () => {
|
|
|
95
98
|
);
|
|
96
99
|
});
|
|
97
100
|
|
|
101
|
+
test("carries the shipment identity the ingest dedup relies on", () => {
|
|
102
|
+
const shipmentId: AnalyticsTableColumn | null =
|
|
103
|
+
model.getTableColumn("shipmentId");
|
|
104
|
+
expect(shipmentId?.type).toBe(TableColumnType.Text);
|
|
105
|
+
// Rows written before the shipment contract read back as "no identity".
|
|
106
|
+
expect(shipmentId?.defaultValue).toBe("");
|
|
107
|
+
|
|
108
|
+
const shipmentChunk: AnalyticsTableColumn | null =
|
|
109
|
+
model.getTableColumn("shipmentChunk");
|
|
110
|
+
expect(shipmentChunk?.type).toBe(TableColumnType.Number);
|
|
111
|
+
expect(shipmentChunk?.defaultValue).toBe(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
98
114
|
test("stores labels as a map with an extracted key column", () => {
|
|
99
115
|
const labels: AnalyticsTableColumn | null = model.getTableColumn("labels");
|
|
100
116
|
expect(labels?.type).toBe(TableColumnType.MapStringString);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JSONArray, JSONObject } from "../../Types/JSON";
|
|
1
|
+
import { JSONArray, JSONObject, ObjectType } from "../../Types/JSON";
|
|
2
2
|
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
3
3
|
import JSONFunctions from "../../Types/JSONFunctions";
|
|
4
4
|
|
|
@@ -262,4 +262,106 @@ describe("JSONFunctions Class", () => {
|
|
|
262
262
|
expect(size).toBeGreaterThanOrEqual(0);
|
|
263
263
|
});
|
|
264
264
|
});
|
|
265
|
+
|
|
266
|
+
describe("serialize and serializeArray Methods", () => {
|
|
267
|
+
test("Passes primitives and null through, and drops undefined keys", () => {
|
|
268
|
+
const input: JSONObject = {
|
|
269
|
+
n: 1,
|
|
270
|
+
s: "x",
|
|
271
|
+
b: true,
|
|
272
|
+
nil: null,
|
|
273
|
+
gone: undefined,
|
|
274
|
+
};
|
|
275
|
+
expect(JSONFunctions.serialize(input)).toEqual({
|
|
276
|
+
n: 1,
|
|
277
|
+
s: "x",
|
|
278
|
+
b: true,
|
|
279
|
+
nil: null,
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("Tags a Date value with its DateTime object type", () => {
|
|
284
|
+
const serialized: JSONObject = JSONFunctions.serialize({
|
|
285
|
+
when: new Date("2023-01-01T00:00:00.000Z"),
|
|
286
|
+
});
|
|
287
|
+
const when: JSONObject = serialized["when"] as JSONObject;
|
|
288
|
+
expect(when["_type"]).toBe(ObjectType.DateTime);
|
|
289
|
+
expect(typeof when["value"]).toBe("string");
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("Serializes nested objects recursively", () => {
|
|
293
|
+
expect(JSONFunctions.serialize({ a: { b: 2 } })).toEqual({ a: { b: 2 } });
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("Serializes each value inside an array", () => {
|
|
297
|
+
expect(JSONFunctions.serialize({ arr: [1, "x", null] })).toEqual({
|
|
298
|
+
arr: [1, "x", null],
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("serializeArray serializes each object in the array", () => {
|
|
303
|
+
const input: JSONArray = [{ a: 1 }, { b: 2 }];
|
|
304
|
+
expect(JSONFunctions.serializeArray(input)).toEqual([{ a: 1 }, { b: 2 }]);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
describe("deserialize and deserializeArray Methods", () => {
|
|
309
|
+
test("Passes primitives and null through", () => {
|
|
310
|
+
const input: JSONObject = { n: 1, s: "x", nil: null };
|
|
311
|
+
expect(JSONFunctions.deserialize(input)).toEqual({
|
|
312
|
+
n: 1,
|
|
313
|
+
s: "x",
|
|
314
|
+
nil: null,
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("Rebuilds a Buffer-tagged value into a Buffer", () => {
|
|
319
|
+
const input: JSONObject = {
|
|
320
|
+
payload: {
|
|
321
|
+
_type: ObjectType.Buffer,
|
|
322
|
+
value: { type: ObjectType.Buffer, data: [1, 2, 3] },
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
const deserialized: JSONObject = JSONFunctions.deserialize(input);
|
|
326
|
+
expect(Buffer.isBuffer(deserialized["payload"])).toBe(true);
|
|
327
|
+
expect(deserialized["payload"]).toEqual(Buffer.from([1, 2, 3]));
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("deserializeArray deserializes each object in the array", () => {
|
|
331
|
+
const input: JSONArray = [{ a: 1 }, { b: 2 }];
|
|
332
|
+
expect(JSONFunctions.deserializeArray(input)).toEqual([
|
|
333
|
+
{ a: 1 },
|
|
334
|
+
{ b: 2 },
|
|
335
|
+
]);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe("serializeValue and deserializeValue Methods", () => {
|
|
340
|
+
test("Pass numbers and non-empty strings through unchanged", () => {
|
|
341
|
+
expect(JSONFunctions.serializeValue(5)).toBe(5);
|
|
342
|
+
expect(JSONFunctions.serializeValue("hi")).toBe("hi");
|
|
343
|
+
expect(JSONFunctions.deserializeValue(5)).toBe(5);
|
|
344
|
+
expect(JSONFunctions.deserializeValue("hi")).toBe("hi");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("Pass null through unchanged", () => {
|
|
348
|
+
expect(JSONFunctions.serializeValue(null)).toBeNull();
|
|
349
|
+
expect(JSONFunctions.deserializeValue(null)).toBeNull();
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe("serialize and deserialize round trip", () => {
|
|
354
|
+
test("A plain object survives a serialize -> JSON -> deserialize round trip", () => {
|
|
355
|
+
const original: JSONObject = {
|
|
356
|
+
a: 1,
|
|
357
|
+
b: "x",
|
|
358
|
+
c: { d: 2 },
|
|
359
|
+
e: [1, 2, 3],
|
|
360
|
+
};
|
|
361
|
+
const roundTripped: JSONObject = JSONFunctions.deserialize(
|
|
362
|
+
JSON.parse(JSON.stringify(JSONFunctions.serialize(original))),
|
|
363
|
+
);
|
|
364
|
+
expect(roundTripped).toEqual(original);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
265
367
|
});
|