Skip to content

Commit 629af1d

Browse files
Implement ItemIndividualQuery; modify query types to store prices as bigint instead of Dinero for easier testing
1 parent 8e93f84 commit 629af1d

17 files changed

+90
-66
lines changed

.kanelrc.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,9 @@ module.exports = {
2727
preDeleteOutputFolder: true,
2828
outputPath: "./src/types/db",
2929

30-
// Postgres bigints are assumed to represent monetary values and converted to Dinero.js objects
30+
// Postgres bigints are mapped to JS bigints
3131
customTypeMap: {
32-
"pg_catalog.tsvector": "string",
33-
"pg_catalog.bpchar": "string",
34-
"pg_catalog.int8": {
35-
name: "Dinero",
36-
typeImports: [
37-
{
38-
name: "Dinero",
39-
path: "dinero.js",
40-
isAbsolute: true,
41-
isDefault: false
42-
}
43-
]
44-
}
32+
"pg_catalog.int8": "bigint"
4533
},
4634

4735
// This implementation will generate flavored instead of branded types.

src/db/DB.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
import {Pool} from "pg";
1+
import {Pool, types} from "pg";
22
import type {QueryResult} from "pg";
33

4+
const UINT8_OID = 20;
5+
types.setTypeParser(UINT8_OID, (val) => BigInt(val));
6+
7+
48
const pool = new Pool({
59
user: process.env.POSTGRES_USER,
610
password: process.env.POSTGRES_PASSWORD,

src/db/queries/ItemIndividualQuery.ts

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,69 @@ import ItemIndividual,
22
{ItemId, ItemIndividualInitializer, ItemIndividualMutator} from "../../types/db/public/ItemIndividual";
33
import {Category} from "../../types/db/public/ValidCategory";
44
import {SimpleCrudQueryable} from "../Queryable";
5+
import * as DB from "../../db/DB";
56

67
const simpleCrudQueries:
78
SimpleCrudQueryable<ItemIndividual, ItemIndividualInitializer, ItemIndividualMutator, ItemId> = {
89
async create(object: ItemIndividualInitializer): Promise<ItemIndividual> {
9-
throw new Error("Method not implemented.");
10+
// Object.keys and Object.values return things in the same order so this is safe
11+
const keys = Object.keys(object);
12+
const values = Object.values(object);
13+
14+
const queryResponse = await DB.query(
15+
`INSERT INTO item_individual (${keys.join(",")})` +
16+
`VALUES (${keys.map((prop, i) => `$${i + 1}`).join(",")})` +
17+
"RETURNING *",
18+
values
19+
);
20+
if (queryResponse.rows.length === 1) {
21+
return queryResponse.rows[0];
22+
} else {
23+
return null;
24+
}
1025
},
1126

1227
async read(itemId: ItemId): Promise<ItemIndividual> {
13-
throw new Error("Method not implemented.");
28+
const queryResponse = await DB.query(
29+
"SELECT * FROM item_individual WHERE item_id=$1",
30+
[itemId]
31+
);
32+
if (queryResponse.rows.length === 1) {
33+
return queryResponse.rows[0];
34+
} else {
35+
return null;
36+
}
1437
},
1538

1639
async readAll(): Promise<ItemIndividual[]> {
17-
throw new Error("Method not implemented.");
40+
const queryResponse = await DB.query("SELECT * FROM item_individual");
41+
return queryResponse.rows;
1842
},
1943

2044
async update(itemId: ItemId, mutateObject: ItemIndividualMutator): Promise<ItemIndividual> {
21-
throw new Error("Method not implemented.");
45+
if (Object.keys(mutateObject).length === 0) {
46+
return null;
47+
}
48+
49+
// Use i+2 for parameter so that $1 is reserved for the item id
50+
const keys = Object.keys(mutateObject).map((prop, i) => `${prop}=$${i + 2}`);
51+
const queryResponse = await DB.query(
52+
`UPDATE item_individual SET ${keys.join(",")} WHERE item_id=$1 RETURNING *`,
53+
[itemId, ...Object.values(mutateObject)]
54+
);
55+
if (queryResponse.rows.length === 1) {
56+
return queryResponse.rows[0];
57+
} else {
58+
return null;
59+
}
2260
},
2361

2462
async delete(itemId: ItemId): Promise<boolean> {
25-
throw new Error("Method not implemented.");
63+
const queryResponse = await DB.query(
64+
"DELETE FROM item_individual WHERE item_id=$1",
65+
[itemId]
66+
);
67+
return queryResponse.rowCount === 1;
2668
}
2769
};
2870

src/types/db/public/ItemIndividual.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {Dinero} from "dinero.js";
21
import {type Category} from "./ValidCategory";
32

43
/** Identifier type for item_individual */
@@ -12,7 +11,7 @@ export default interface ItemIndividual {
1211

1312
description: string;
1413

15-
price: Dinero;
14+
price: bigint;
1615

1716
category: Category;
1817

@@ -35,7 +34,7 @@ export interface ItemIndividualInitializer {
3534

3635
description: string;
3736

38-
price: Dinero;
37+
price: bigint;
3938

4039
category: Category;
4140

@@ -58,7 +57,7 @@ export interface ItemIndividualMutator {
5857

5958
description?: string;
6059

61-
price?: Dinero;
60+
price?: bigint;
6261

6362
category?: Category;
6463

src/types/db/public/Reimbursement.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {Dinero} from "dinero.js";
21
import {type UserId} from "./CsssUser";
32

43
/** Identifier type for reimbursement */
@@ -10,7 +9,7 @@ export default interface Reimbursement {
109

1110
receipt_img_url: string;
1211

13-
purchase_total: Dinero;
12+
purchase_total: bigint;
1413

1514
purchase_date: Date;
1615

@@ -23,7 +22,7 @@ export default interface Reimbursement {
2322
export interface ReimbursementInitializer {
2423
receipt_img_url: string;
2524

26-
purchase_total: Dinero;
25+
purchase_total: bigint;
2726

2827
purchase_date: Date;
2928

@@ -37,7 +36,7 @@ export interface ReimbursementInitializer {
3736
export interface ReimbursementMutator {
3837
receipt_img_url?: string;
3938

40-
purchase_total?: Dinero;
39+
purchase_total?: bigint;
4140

4241
purchase_date?: Date;
4342

src/types/db/public/Transaction.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import {Dinero} from "dinero.js";
2-
31
/** Identifier type for transaction */
42
export type TransactionId = number & {__flavor?: "TransactionId"};
53

64
/** Represents the table public.transaction */
75
export default interface Transaction {
86
transaction_id: TransactionId;
97

10-
total: Dinero;
8+
total: bigint;
119

1210
transaction_time: Date | null;
1311

@@ -16,7 +14,7 @@ export default interface Transaction {
1614

1715
/** Represents the initializer for the table public.transaction */
1816
export interface TransactionInitializer {
19-
total: Dinero;
17+
total: bigint;
2018

2119
/** Default value: CURRENT_TIMESTAMP */
2220
transaction_time?: Date | null;
@@ -26,7 +24,7 @@ export interface TransactionInitializer {
2624

2725
/** Represents the mutator for the table public.transaction */
2826
export interface TransactionMutator {
29-
total?: Dinero;
27+
total?: bigint;
3028

3129
transaction_time?: Date | null;
3230

test/db/queries/CompositeCrudQueryable.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ export const testUpdate = <T, TInit, TMut, PK1, PK2>(
7777
it("returns null when updating nonexistent queryable", async () => {
7878
expect(await Queryable.update(nonexistentId1, nonexistentId2, testMutator)).to.be.null;
7979
});
80-
it("does not modify queryable if given empty mutator", async () => {
80+
it("returns null if given empty mutator", async () => {
8181
const createdItem = await Queryable.create(testInitializer);
82-
expect(await Queryable.update(getId1(createdItem), getId2(createdItem), {})).to.deep.equal(createdItem);
82+
expect(await Queryable.update(getId1(createdItem), getId2(createdItem), {})).to.be.null;
8383

8484
// cleanup
8585
await Queryable.delete(getId1(createdItem), getId2(createdItem));

test/db/queries/CsssUser.spec.ts renamed to test/db/queries/CsssUserQuery.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const testCsssInitializer: CsssUserInitializer = {
1313
phone_number: "9998887777"
1414
};
1515

16-
describe("ItemIndividual Query Tests", () => {
16+
describe("CsssUser Query Tests", () => {
1717

1818
testCreate(CsssUserQuery, {
1919
testInitializer: testCsssInitializer,

test/db/queries/ItemBox.spec.ts renamed to test/db/queries/ItemBoxQuery.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const testItemBoxInitializer: ItemBoxInitializer = {
88
quantity_per_box: 6
99
};
1010

11-
describe("ItemIndividual Query Tests", () => {
11+
describe("ItemBox Query Tests", () => {
1212

1313
testCreate(ItemBoxQuery, {
1414
testInitializer: testItemBoxInitializer,

test/db/queries/ItemIndividualQuery.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import {expect} from "chai";
22
import ItemIndividualQuery from "../../../src/db/queries/ItemIndividualQuery";
33
import {ItemIndividualInitializer, ItemIndividualMutator} from "../../../src/types/db/public/ItemIndividual";
4-
import Dinero from "dinero.js";
54
import * as TestItems from "../test_objs/ItemIndividual";
65
import {testCreate, testDelete, testRead, testReadAll, testUpdate} from "./SimpleCrudQueryable";
76

87
const testItemInitializer: ItemIndividualInitializer = {
98
name: "Welch's Fruit Snacks",
109
description: "Made with real fruit",
11-
price: Dinero({amount: 75, currency: "CAD"}),
10+
price: BigInt(75),
1211
category: "food",
1312
img_url: "url",
1413
reservable: false,
1514
quantity_remaining: 5,
1615
low_stock_threshold: 5,
17-
last_restocked: new Date("2024-01-24"),
16+
last_restocked: new Date("2024-01-24T08:00:00.000Z"),
1817
max_quantity_per_transaction: 2
1918
};
2019

@@ -34,9 +33,9 @@ describe("ItemIndividual Query Tests", () => {
3433
testReadAll(ItemIndividualQuery, Object.values(TestItems));
3534

3635
const itemMutator: ItemIndividualMutator = {
37-
price: Dinero({amount: 50, currency: "CAD"}),
36+
price: BigInt(50),
3837
quantity_remaining: 25,
39-
last_restocked: new Date("2024-02-01"),
38+
last_restocked: new Date("2024-02-01T08:00:00.000Z"),
4039
};
4140
testUpdate(ItemIndividualQuery, {
4241
testInitializer: testItemInitializer,

0 commit comments

Comments
 (0)