-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finish implementing Queryable tests #15
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
4368c3c
Finish implementing ItemIndividualQuery-specific functions
daniel-panhead 3a869e7
Implement all ReimbursementItemBoxQuery functions
daniel-panhead 2fcf2af
Add nonexistent read check to CompositeCrudQueryable tests
daniel-panhead cfba061
Refactor crud queryable interfaces out to separate files
daniel-panhead 09d40b0
Factor simple crud queries out into generic functions
daniel-panhead cf6d3c6
Factor composiste crud queries out into generic functions
daniel-panhead 6e449ce
Convert crud queryables to be classes and inherit default crud functions
daniel-panhead aff358a
Fix ReimbursementQuery test datetimes; ValidCategoryQuery updating sh…
daniel-panhead 0dc5d21
Change crud queryable fields to be protected instead of private
daniel-panhead 097894f
Finish implementing TransactionItemQuery and add guards to composite …
daniel-panhead 6947312
Finish implementing CsssUserQuery
daniel-panhead File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import * as DB from "../db/DB"; | ||
|
||
export class CompositeCrudQueryable<T, TInit, TMut, PK1, PK2> { | ||
protected tableName: string; | ||
protected pk1Name: string; | ||
protected pk2Name: string; | ||
|
||
constructor(tableName: string, pk1Name: string, pk2Name: string) { | ||
this.tableName = tableName; | ||
this.pk1Name = pk1Name; | ||
this.pk2Name = pk2Name; | ||
} | ||
|
||
/** | ||
* Creates a new entry for the given object in the database. | ||
* @param object Initializer of the object | ||
* @returns Promise resolving to the given initialized object if successful | ||
*/ | ||
public create = async (object: TInit): Promise<T> => { | ||
// Object.keys and Object.values return things in the same order so this is safe | ||
const keys = Object.keys(object); | ||
const values = Object.values(object); | ||
|
||
const queryResponse = await DB.query( | ||
`INSERT INTO ${this.tableName} (${keys.join(",")})` + | ||
`VALUES (${keys.map((prop, i) => `$${i + 1}`).join(",")})` + | ||
"RETURNING *", | ||
values | ||
); | ||
if (queryResponse.rows.length === 1) { | ||
return queryResponse.rows[0]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
|
||
/** | ||
* Reads the database and returns the object with the given composite key. | ||
* @param pk1 First foreign key of the object that forms the composite key | ||
* @param pk2 Second foreign key of the object that forms the composite key | ||
* @returns Promise resolving to object with given composite key, or null if no object is found | ||
*/ | ||
public read = async (pk1: PK1, pk2: PK2): Promise<T> => { | ||
const queryResponse = await DB.query( | ||
`SELECT * FROM ${this.tableName} WHERE ${this.pk1Name}=$1 AND ${this.pk2Name}=$2`, | ||
[pk1, pk2] | ||
); | ||
if (queryResponse.rows.length === 1) { | ||
return queryResponse.rows[0]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
|
||
/** | ||
* Reads the database for all entries of the object. | ||
* @returns Promise resolving to all entries of the object in its table in the database | ||
*/ | ||
public readAll = async (): Promise<T[]> => { | ||
const queryResponse = await DB.query(`SELECT * FROM ${this.tableName}`); | ||
return queryResponse.rows; | ||
}; | ||
|
||
/** | ||
* Updates an existing object with the given composite key. | ||
* @param pk1 First foreign key of the object that forms the composite key | ||
* @param pk2 Second foreign key of the object that forms the composite key | ||
* @param mutateProps Mutator of the object containing desired new properties | ||
* @returns Promise resolving to the updated object, or null if no object is found | ||
*/ | ||
public update = async (pk1: PK1, pk2: PK2, mutateObject: TMut): Promise<T> => { | ||
if (Object.keys(mutateObject).length === 0) { | ||
return null; | ||
} | ||
|
||
// Use i+3 for parameter so that $1 and $2 are reserved for the PKs | ||
const keys = Object.keys(mutateObject).map((prop, i) => `${prop}=$${i + 3}`); | ||
const queryResponse = await DB.query( | ||
`UPDATE ${this.tableName} SET ${keys.join(",")}` + | ||
` WHERE ${this.pk1Name}=$1 AND ${this.pk2Name}=$2 RETURNING *`, | ||
[pk1, pk2, ...Object.values(mutateObject)] | ||
); | ||
if (queryResponse.rows.length === 1) { | ||
return queryResponse.rows[0]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
|
||
/** | ||
* Deletes the object in the database with the given composite key. | ||
* @param pk1 First foreign key of the object that forms the composite key | ||
* @param pk2 Second foreign key of the object that forms the composite key | ||
* @returns Promise resolving to boolean indicating whether any rows were deleted | ||
*/ | ||
public delete = async (pk1: PK1, pk2: PK2): Promise<boolean> => { | ||
const queryResponse = await DB.query( | ||
`DELETE FROM ${this.tableName} WHERE ${this.pk1Name}=$1 AND ${this.pk2Name}=$2`, | ||
[pk1, pk2] | ||
); | ||
return queryResponse.rowCount === 1; | ||
}; | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import * as DB from "../db/DB"; | ||
|
||
export class SimpleCrudQueryable<T, TInit, TMut, PK> { | ||
protected tableName: string; | ||
protected pkName: string; | ||
|
||
constructor(tableName: string, pkName: string) { | ||
this.tableName = tableName; | ||
this.pkName = pkName; | ||
} | ||
|
||
/** | ||
* Creates a new entry for the given object in the database. | ||
* @param object Initializer of the object | ||
* @returns Promise resolving to the given initialized object if successful | ||
*/ | ||
public create = async (object: TInit): Promise<T> => { | ||
// Object.keys and Object.values return things in the same order so this is safe | ||
const keys = Object.keys(object); | ||
const values = Object.values(object); | ||
|
||
const queryResponse = await DB.query( | ||
`INSERT INTO ${this.tableName} (${keys.join(",")})` + | ||
`VALUES (${keys.map((prop, i) => `$${i + 1}`).join(",")})` + | ||
"RETURNING *", | ||
values | ||
); | ||
if (queryResponse.rows.length === 1) { | ||
return queryResponse.rows[0]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
|
||
/** | ||
* Reads the database and returns the object with the given primary key. | ||
* @param primaryKey Primary key of the object | ||
* @returns Promise resolving to object with given primary key, or null if no object is found | ||
*/ | ||
public read = async (primaryKey: PK): Promise<T> => { | ||
const queryResponse = await DB.query( | ||
`SELECT * FROM ${this.tableName} WHERE ${this.pkName}=$1`, | ||
[primaryKey] | ||
); | ||
if (queryResponse.rows.length === 1) { | ||
return queryResponse.rows[0]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
|
||
/** | ||
* Reads the database for all entries of the object. | ||
* @returns Promise resolving to all entries of the object in its table in the database | ||
*/ | ||
public readAll = async (): Promise<T[]> => { | ||
const queryResponse = await DB.query(`SELECT * FROM ${this.tableName}`); | ||
return queryResponse.rows; | ||
}; | ||
|
||
/** | ||
* Updates an existing object with the given primary key. | ||
* @param primaryKey Primary key of the object | ||
* @param mutateProps Mutator of the object containing desired new properties | ||
* @returns Promise resolving to the updated object, or null if no object is found | ||
*/ | ||
public update = async (primaryKey: PK, mutateObject: TMut): Promise<T> => { | ||
if (Object.keys(mutateObject).length === 0) { | ||
return null; | ||
} | ||
|
||
// Use i+2 for parameter so that $1 is reserved for the item id | ||
const keys = Object.keys(mutateObject).map((prop, i) => `${prop}=$${i + 2}`); | ||
const queryResponse = await DB.query( | ||
`UPDATE ${this.tableName} SET ${keys.join(",")} WHERE ${this.pkName}=$1 RETURNING *`, | ||
[primaryKey, ...Object.values(mutateObject)] | ||
); | ||
if (queryResponse.rows.length === 1) { | ||
return queryResponse.rows[0]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
|
||
/** | ||
* Deletes the object in the database with the given primary key. | ||
* @param primaryKey Primary key of the object | ||
* @returns Promise resolving to boolean indicating whether any rows were deleted | ||
*/ | ||
public delete = async (primaryKey: PK): Promise<boolean> => { | ||
const queryResponse = await DB.query( | ||
`DELETE FROM ${this.tableName} WHERE ${this.pkName}=$1`, | ||
[primaryKey] | ||
); | ||
return queryResponse.rowCount === 1; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,34 @@ | ||
|
||
import CsssUser, | ||
{UserId, CsssUserInitializer, CsssUserMutator} from "../../types/db/public/CsssUser"; | ||
import {SimpleCrudQueryable} from "../Queryable"; | ||
import {SimpleCrudQueryable} from "../SimpleCrudQueryable"; | ||
import * as DB from "../../db/DB"; | ||
|
||
const simpleCrudQueries: | ||
SimpleCrudQueryable<CsssUser, CsssUserInitializer, CsssUserMutator, UserId> = { | ||
async create(object: CsssUserInitializer): Promise<CsssUser> { | ||
throw new Error("Method not implemented."); | ||
}, | ||
const tableName = "csss_user"; | ||
const pkName = "user_id"; | ||
|
||
async read(userId: UserId): Promise<CsssUser> { | ||
throw new Error("Method not implemented."); | ||
}, | ||
|
||
// should we allow? | ||
async readAll(): Promise<CsssUser[]> { | ||
throw new Error("Method not implemented."); | ||
}, | ||
|
||
async update(userId: UserId, mutateObject: CsssUserMutator): Promise<CsssUser> { | ||
throw new Error("Method not implemented."); | ||
}, | ||
|
||
async delete(userId: UserId): Promise<boolean> { | ||
throw new Error("Method not implemented."); | ||
class CsssUserQuery extends SimpleCrudQueryable<CsssUser, CsssUserInitializer, CsssUserMutator, UserId> { | ||
constructor() { | ||
super(tableName, pkName); | ||
} | ||
}; | ||
|
||
const csssUserQueries = { | ||
/** | ||
* Tries to authenticate the user with the given credentials. | ||
* @param email Email of the given user | ||
* @param password Password hash of the given user | ||
* @returns Promise resolving to the CsssUser with the given credentials, or null if no user found | ||
*/ | ||
async authenticateUser(email: string, password: string): Promise<CsssUser> { | ||
throw new Error("Method not implemented."); | ||
} | ||
}; | ||
|
||
export default { | ||
...simpleCrudQueries, | ||
...csssUserQueries | ||
}; | ||
public authenticateUser = async (email: string, password: string): Promise<CsssUser> => { | ||
const queryResponse = await DB.query( | ||
`SELECT * FROM ${this.tableName} WHERE email=$1 AND password=$2`, | ||
[email, password] | ||
); | ||
if (queryResponse.rows.length === 1) { | ||
return queryResponse.rows[0]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
} | ||
|
||
export default new CsssUserQuery(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,14 @@ | ||
|
||
import ItemBox, {ItemBoxId, ItemBoxInitializer, ItemBoxMutator} from "../../types/db/public/ItemBox"; | ||
import {SimpleCrudQueryable} from "../Queryable"; | ||
import {SimpleCrudQueryable} from "../SimpleCrudQueryable"; | ||
|
||
const simpleCrudQueries: | ||
SimpleCrudQueryable<ItemBox, ItemBoxInitializer, ItemBoxMutator, ItemBoxId> = { | ||
async create(object: ItemBoxInitializer): Promise<ItemBox> { | ||
throw new Error("Method not implemented."); | ||
}, | ||
const tableName = "item_box"; | ||
const pkName = "item_box_id"; | ||
|
||
async read(itemBoxId: ItemBoxId): Promise<ItemBox> { | ||
throw new Error("Method not implemented."); | ||
}, | ||
|
||
async readAll(): Promise<ItemBox[]> { | ||
throw new Error("Method not implemented."); | ||
}, | ||
|
||
async update(itemBoxId: ItemBoxId, mutateObject: ItemBoxMutator): Promise<ItemBox> { | ||
throw new Error("Method not implemented."); | ||
}, | ||
|
||
async delete(itemBoxId: ItemBoxId): Promise<boolean> { | ||
throw new Error("Method not implemented."); | ||
class ItemBoxQuery extends SimpleCrudQueryable<ItemBox, ItemBoxInitializer, ItemBoxMutator, ItemBoxId> { | ||
constructor() { | ||
super(tableName, pkName); | ||
} | ||
}; | ||
} | ||
|
||
export default { | ||
...simpleCrudQueries | ||
}; | ||
export default new ItemBoxQuery(); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivan-fediaev