attempted to refactor db seed, still needs more updating

This commit is contained in:
Mikayla Dobson
2022-11-26 18:26:32 -06:00
parent 9bd1704da9
commit 9d0c8a8782
22 changed files with 340 additions and 68 deletions

View File

@@ -1,11 +1,12 @@
import { IUser, IUserAuth } from "../schemas";
import { User } from "../models/user";
import { Collection } from "../models/collection";
import createError from "http-errors";
import bcrypt from "bcrypt";
import now from "../util/now";
const UserInstance = new User();
const CollectionInstance = new Collection();
export default class AuthService {
// methods for local strategies
async register(data: IUser) {
@@ -19,8 +20,9 @@ export default class AuthService {
try {
const user = await UserInstance.getOneByEmail(email);
if (user) throw createError('409', 'Email already in use');
bcrypt.genSalt(10, (err, salt) => {
// hash password and create new user record
return bcrypt.genSalt(10, (err, salt) => {
if (err) throw err;
bcrypt.hash(password!, salt, async (err, hash) => {
if (err) throw err;
@@ -29,7 +31,18 @@ export default class AuthService {
password: hash
}
const result = await UserInstance.post(newData);
const result: IUser = await UserInstance.post(newData);
// basic profile setup
await CollectionInstance.post({
name: `${data.firstname}'s Collection`,
active: true,
ismaincollection: true,
ownerid: result.id!.toString(),
datecreated: now,
datemodified: now
});
return result;
})
})

View File

@@ -4,7 +4,7 @@ import { Collection } from "../models/collection";
const CollectionInstance = new Collection();
export default class CollectionCtl {
async getOne(id: string) {
async getOne(id: number | string) {
const result = await CollectionInstance.getOne(id);
if (!result) throw createError('404', 'Collection not found');
return result;
@@ -15,6 +15,12 @@ export default class CollectionCtl {
if (!result) throw createError('404', 'No collections found');
return result;
}
async getUserDefault(id: number | string) {
const result = await CollectionInstance.getUserDefault(id);
if (!result) throw createError('404', "No default collection found. Contact an admin, this isn't supposed to happen");
return result;
}
async post(data: ICollection) {
const result = await CollectionInstance.post(data);
@@ -22,19 +28,19 @@ export default class CollectionCtl {
return result;
}
async getSubscriptions(userid: string) {
async getSubscriptions(userid: number | string) {
const result = await CollectionInstance.getSubscriptions(userid);
if (!result) throw createError('404', 'No subscriptions found');
return result;
}
async postSubscription(collectionid: string, userid: string) {
async postSubscription(collectionid: number | string, userid: number | string) {
const { ok, code, data } = await CollectionInstance.postSubscription(collectionid, userid);
if (ok) {
return data;
} else {
throw createError(code, data);
throw createError(code, data as string);
}
}
}

View File

@@ -14,6 +14,24 @@ export default class RecipeCtl {
}
}
async getAllAuthored(id: string) {
try {
const result = await RecipeInstance.getAllAuthored(id);
if (!result) throw createError('404', "No recipes found");
return result;
} catch (e: any) {
throw new Error(e);
}
}
async getAllAccessible(id: string) {
try {
} catch (e: any) {
throw new Error(e);
}
}
async updateOne(id: string, data: IRecipe) {
try {
const result = await RecipeInstance.updateOneByID(id, data);
@@ -24,9 +42,9 @@ export default class RecipeCtl {
}
}
async post(data: IRecipe) {
async post(userid: string, data: IRecipe) {
try {
const result = await RecipeInstance.post(data);
const result = await RecipeInstance.post(userid, data);
if (!result) throw createError('400', "Bad request");
return result;
} catch (error: any) {

View File

@@ -24,7 +24,7 @@ export default class UserCtl {
}
}
async getOne(id: string) {
async getOne(id: number | string) {
try {
const user = await UserInstance.getOneByID(id);
if (!user) throw createError(404, "User not found");
@@ -34,7 +34,7 @@ export default class UserCtl {
}
}
async updateOne(id: string, data: IUser) {
async updateOne(id: number | string, data: IUser) {
try {
const result = await UserInstance.updateOneByID(id, data);
if (!result) throw createError(400, "Bad request");
@@ -44,7 +44,7 @@ export default class UserCtl {
}
}
async getFriends(id: string) {
async getFriends(id: number | string) {
try {
const result = await UserInstance.getFriends(id);
if (!result) throw createError(404, "You have no friends");
@@ -54,7 +54,7 @@ export default class UserCtl {
}
}
async getFriendshipByID(id: string, userid: string) {
async getFriendshipByID(id: number | string, userid: number | string) {
try {
const { ok, code, result } = await UserInstance.getFriendshipByID(id, userid);
if (ok) return result;
@@ -64,7 +64,7 @@ export default class UserCtl {
}
}
async addFriendship(userid: string, targetid: string) {
async addFriendship(userid: number | string, targetid: number | string) {
try {
const result = await UserInstance.addFriendship(userid, targetid);
if (!result) throw createError(400, "something went wrong");
@@ -74,11 +74,11 @@ export default class UserCtl {
}
}
async updateFriendship(id: string, data: { active: boolean, pending: boolean, dateterminated?: string }) {
async updateFriendship(id: number | string, userid: number | string, data: { active: boolean, pending: boolean, dateterminated?: string }) {
try {
const result = await UserInstance.updateFriendship(id, data);
if (!result) throw createError(400, "something went wrong");
return result;
const { ok, code, result } = await UserInstance.updateFriendship(id, userid, data);
if (ok) return result;
throw createError(code, result);
} catch (e: any) {
throw new Error(e);
}

142
server/db/newPopulate.ts Normal file
View File

@@ -0,0 +1,142 @@
import pool from ".";
import { IIngredient, IRecipe, IUser } from "../schemas";
import { User } from "../models/user";
import { Recipe } from "../models/recipe";
import { Collection } from "../models/collection";
import { Ingredient } from "../models/ingredient";
import AuthService from "../auth";
import now from "../util/now";
const auth = new AuthService();
const user = new User();
const collection = new Collection();
const ingredient = new Ingredient();
export default async function populate() {
// register new users; initialize default collection
const usr1: IUser = {
firstname: "Mikayla",
lastname: "Dobson",
handle: "innocuoussymmetry",
password: "coolpassword",
email: "mikayla@mikayla.com",
active: true,
isadmin: true
}
const usr2: IUser = {
firstname: "Emily",
lastname: "Dobson",
handle: "emjdobson",
password: "coolpassword",
email: "emily@emily.com",
active: true,
isadmin: false
}
const usr3: IUser = {
firstname: "Montanna",
lastname: "Dobson",
handle: "delayedlemon",
password: "coolpassword",
email: "montanna@montanna.com",
active: true,
isadmin: false
}
const usr4: IUser = {
firstname: 'someother',
lastname: 'person',
handle: 'someperson',
password: 'coolpassword',
email: 'someperson@email.com',
active: false,
isadmin: false
}
for (let usr of [usr1, usr2, usr3, usr4]) {
await auth.register(usr);
}
/*
// recipes onto new user profiles; insert into new default collections
const rec1: IRecipe = {
name: "Pad Thai",
preptime: '1 hour',
authoruserid: 1
}
const rec2: IRecipe = {
name: "Bo ssam",
preptime: '8 hours',
authoruserid: 3
}
const rec3: IRecipe = {
name: "Banana bread",
preptime: '90 minutes',
authoruserid: 2
}
const rec4: IRecipe = {
name: "garlic confit",
preptime: "2 hours",
authoruserid: 3
}
for (let rec of [rec1, rec2, rec3, rec4]) {
await recipe.post(rec.authoruserid as string, rec);
}
*/
// set up a couple of friendships
// await user.addFriendship(1, 2);
// await user.addFriendship(1, 3);
// await user.addFriendship(2, 3);
// await user.updateFriendship(1, 2, {
// active: true,
// pending: false
// });
// await user.updateFriendship(2, 3, {
// active: true,
// pending: false
// })
// // request and validate subscriptions
// const usr1default = await collection.getUserDefault(1);
// await collection.postSubscription(usr1default.id, 2);
// const usr3default = await collection.getUserDefault(3);
// await collection.postSubscription(usr3default.id, 1);
// add some ingredients
const ing1: IIngredient = {
name: "cucumber",
description: "vegetal",
datecreated: now,
createdbyid: 1
}
const ing2: IIngredient = {
name: "tofu",
description: "soy protein",
datecreated: now,
createdbyid: 1
}
const ing3: IIngredient = {
name: 'garlic',
description: "lifeblood",
datecreated: now,
createdbyid: 3
}
for (let ing of [ing1, ing2, ing3]) {
await ingredient.post(ing);
}
}

View File

@@ -59,7 +59,7 @@ export default async function populate() {
const populateFriendships = `
INSERT INTO recipin.cmp_userfriendships
(datecreated, active, pending, firstuserid, seconduserid)
(datecreated, active, pending, senderid, targetid)
VALUES
($1, true, false, 1, 2),
($1, true, false, 1, 4),

View File

@@ -1,4 +1,5 @@
import dotenv from 'dotenv';
import newPopulate from './newPopulate';
import populate from "./populate";
import pool from ".";
import fs from "fs";
@@ -41,6 +42,7 @@ dotenv.config();
console.log("Database seed successful. Attempting to populate...");
await populate();
// await newPopulate();
process.exit(1);
})();

View File

@@ -4,6 +4,6 @@ CREATE TABLE IF NOT EXISTS recipin.cmp_userfriendships (
active boolean NOT NULL,
pending boolean NOT NULL,
dateterminated varchar,
firstuserid int REFERENCES recipin.appusers (id),
seconduserid int REFERENCES recipin.appusers (id)
senderid int REFERENCES recipin.appusers (id),
targetid int REFERENCES recipin.appusers (id)
);

View File

@@ -1,5 +1,6 @@
CREATE TABLE IF NOT EXISTS recipin.cmp_usersubscriptions (
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
collectionid int REFERENCES recipin.collection (id),
usermemberid int REFERENCES recipin.appusers (id)
usermemberid int REFERENCES recipin.appusers (id),
active boolean NOT NULL
);

View File

@@ -5,5 +5,5 @@ CREATE TABLE IF NOT EXISTS recipin.recipe (
datecreated varchar NOT NULL,
datemodified varchar NOT NULL,
description varchar,
authoruserid int REFERENCES recipin.appusers (id) NOT NULL
authoruserid int REFERENCES recipin.appusers (id)
);

View File

@@ -8,6 +8,6 @@ SELECT
recipin.appusers.email
FROM recipin.cmp_userfriendships
INNER JOIN recipin.appusers
ON recipin.appusers.id = recipin.cmp_userfriendships.seconduserid
WHERE firstuserid = $1 OR seconduserid = $1
ON recipin.appusers.id = recipin.cmp_userfriendships.targetid
WHERE senderid = $1 OR targetid = $1
AND cmp_userfriendships.active = true;

View File

@@ -0,0 +1,2 @@
SELECT * FROM recipin.recipe
WHERE authorid = $1;

View File

@@ -8,7 +8,7 @@ SELECT
recipin.appusers.email
FROM recipin.cmp_userfriendships
INNER JOIN recipin.appusers
ON recipin.appusers.id = recipin.cmp_userfriendships.firstuserid
ON recipin.appusers.id = recipin.cmp_userfriendships.senderid
WHERE recipin.cmp_userfriendships.id = $1
UNION
SELECT
@@ -21,5 +21,5 @@ SELECT
recipin.appusers.email
FROM recipin.cmp_userfriendships
INNER JOIN recipin.appusers
ON recipin.appusers.id = recipin.cmp_userfriendships.seconduserid
ON recipin.appusers.id = recipin.cmp_userfriendships.targetid
WHERE recipin.cmp_userfriendships.id = $1;

View File

@@ -6,7 +6,7 @@ import pool from "../db";
import fs from 'fs';
const UserInstance = new User();
export class Collection {
async getOne(id: string) {
async getOne(id: number | string) {
try {
const statement = `SELECT * FROM recipin.collection WHERE id = $1`;
const values = [id];
@@ -18,6 +18,21 @@ export class Collection {
}
}
async getUserDefault(id: number | string) {
try {
const statement = `
SELECT * FROM recipin.collection
WHERE ownerid = $1
AND ismaincollection = true;
`
const result = await pool.query(statement, [id]);
if (result.rows.length) return result.rows[0];
return null;
} catch (e: any) {
throw new Error(e);
}
}
async getAll() {
// requires clearance
try {
@@ -31,6 +46,8 @@ export class Collection {
}
async post(data: ICollection) {
console.log('new default collection');
console.log(data);
const { name, active, ismaincollection, ownerid } = data;
try {
const statement = `
@@ -48,7 +65,7 @@ export class Collection {
}
}
async getSubscriptions(userid: string) {
async getSubscriptions(userid: number | string) {
try {
const sql = fs.readFileSync(appRoot + '/db/sql/get/getsubscriptions.sql').toString();
console.log(sql);
@@ -60,7 +77,7 @@ export class Collection {
}
}
async postSubscription(collectionid: string, userid: string): Promise<{ ok: boolean, code: number, data: string | any[] }> {
async postSubscription(collectionid: number | string, userid: number | string): Promise<{ ok: boolean, code: number, data: number | string | any[] }> {
try {
// ensure user exists
const user: IUser | null = await UserInstance.getOneByID(userid);
@@ -83,7 +100,8 @@ export class Collection {
}
// ensure a user cannot subscribe to their own collection
if (target.ownerid == parseInt(userid)) {
let typedUserID: number = (userid == typeof 'string') ? parseInt(userid) : userid as number;
if (target.ownerid == typedUserID) {
return {
ok: false,
code: 403,
@@ -99,7 +117,7 @@ export class Collection {
const subscriptionResult = await pool.query(allSubscriptions, [collectionid]);
if (subscriptionResult.rows?.length) {
for (let row of subscriptionResult.rows) {
if (row.usermemberid == parseInt(userid)) {
if (row.usermemberid == typedUserID) {
return {
ok: false,
code: 403,
@@ -112,8 +130,8 @@ export class Collection {
// finally, execute insertion
const statement = `
INSERT INTO recipin.cmp_usersubscriptions
(collectionid, usermemberid)
VALUES ($1, $2)
(collectionid, usermemberid, active)
VALUES ($1, $2, true)
RETURNING *;
`

View File

@@ -1,5 +1,6 @@
import { IIngredient } from "../schemas";
import pool from "../db";
import now from "../util/now";
export class Ingredient {
async getAll() {
@@ -28,9 +29,9 @@ export class Ingredient {
try {
const statement = `
INSERT INTO recipin.ingredient
(name, description)
VALUES ($1, $2) RETURNING *`;
const values = [data.name, data.description];
(name, description, datecreated)
VALUES ($1, $2, $3) RETURNING *`;
const values = [data.name, data.description, data.datecreated || now];
const result = await pool.query(statement, values);
if (result.rows.length) return result.rows[0];
return null;

View File

@@ -1,8 +1,8 @@
import { IRecipe } from "../schemas";
import pgPromise from "pg-promise";
import pool from "../db";
const pgp = pgPromise({ capSQL: true });
import { CollectionCtl } from "../controllers";
import now from "../util/now";
const CollectionInstance = new CollectionCtl();
export class Recipe {
async getOneByID(id: string) {
@@ -17,9 +17,24 @@ export class Recipe {
}
}
async getAllByUserID(id: string) {
async getAllAuthored(id: string) {
try {
// to do: use setupbrowser.sql to setup the recipe browser
const statement = `SELECT * FROM recipin.recipe WHERE authoruserid = $1`;
const result = await pool.query(statement, [id]);
if (result.rows.length) return result.rows;
return null;
} catch (e: any) {
throw new Error(e);
}
}
async getAllAccessible(id: string) {
}
async fetchRecipesByCollection(collectionid: string) {
try {
} catch (e: any) {
throw new Error(e);
}
@@ -45,15 +60,32 @@ export class Recipe {
}
}
async post(data: IRecipe) {
async post(userid: string, data: IRecipe) {
const { name, description, preptime } = data;
try {
const statement = `INSERT INTO recipin.recipe (name, description, preptime, authoruserid) VALUES ($1, $2, $3, (SELECT id FROM recipin.appusers WHERE id = 1)) RETURNING *;`
const values = [name, description, preptime];
// create recipe itself
const statement = `
INSERT INTO recipin.recipe
(name, description, preptime, authoruserid, datecreated, datemodified)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *;
`
const values = [name, description, preptime, userid, now, now];
const result = await pool.query(statement, values);
if (result.rows) return result.rows[0];
return null;
if (!result.rows.length) return null;
// associate recipe with default collection once created
const collection = await CollectionInstance.getUserDefault(userid);
const associateToCollection = `
INSERT INTO recipin.cmp_recipecollection
(recipeid, collectionid)
VALUES ($1, $2) RETURNING *
`
const associateResult = await pool.query(associateToCollection, [result.rows[0].id, collection.id]);
if (!associateResult.rows.length) return null;
return { recipe: result.rows[0], collection: associateResult.rows[0] }
} catch (error: any) {
throw new Error(error);
}

View File

@@ -17,7 +17,7 @@ export class User {
}
}
async getOneByID(id: string) {
async getOneByID(id: number | string) {
try {
const statement = `SELECT * FROM recipin.appusers WHERE id = $1`;
const values = [id];
@@ -29,7 +29,7 @@ export class User {
}
}
async getOneByEmail(email: string) {
async getOneByEmail(email: number | string) {
try {
const statement = `SELECT * FROM recipin.appusers WHERE email = $1`;
const result = await pool.query(statement, [email]);
@@ -41,7 +41,7 @@ export class User {
}
}
async updateOneByID(id: string, data: IUser) {
async updateOneByID(id: number | string, data: IUser) {
try {
const statement = `
UPDATE recipin.appusers
@@ -85,14 +85,14 @@ export class User {
`;
const params = [firstname, lastname, handle, email, password, active, isadmin, datecreated, datemodified];
const result = await pool.query(statement, params);
if (result.rows.length) return result.rows;
if (result.rows.length) return result.rows[0];
return null;
} catch (error: any) {
throw new Error(error);
}
}
async getFriends(id: string) {
async getFriends(id: number | string) {
try {
const sql = fs.readFileSync(appRoot + '/db/sql/derived/friendships.sql').toString();
const result = await pool.query(sql, [id]);
@@ -103,14 +103,14 @@ export class User {
}
}
async getFriendshipByID(id: string, userid: string) {
async getFriendshipByID(id: number | string, userid: number | string) {
try {
const statement = `SELECT * FROM recipin.cmp_userfriendships WHERE id = $1`;
const result = await pool.query(statement, [id]);
if (result.rows.length) {
const row = result.rows[0];
if (row.firstuserid == userid || row.seconduserid == userid) {
if (row.senderid == userid || row.targetid == userid) {
const sql = fs.readFileSync(appRoot + '/db/sql/get/friendshipbyid.sql').toString();
const formattedResult = await pool.query(sql, [id]);
if (formattedResult.rows.length) return { ok: true, code: 200, result: formattedResult.rows }
@@ -125,11 +125,11 @@ export class User {
}
}
async addFriendship(userid: string, targetid: string) {
async addFriendship(userid: number | string, targetid: number | string) {
try {
const statement = `
INSERT INTO recipin.cmp_userfriendships
(datecreated, active, pending, firstuserid, seconduserid)
(datecreated, active, pending, senderid, targetid)
VALUES ($1, false, true, $2, $3)
RETURNING *;
`
@@ -144,8 +144,15 @@ export class User {
}
}
async updateFriendship(id: string, data: { active: boolean, pending: boolean, dateterminated?: string }) {
async updateFriendship(id: number | string, userid: number | string, data: { active: boolean, pending: boolean, dateterminated?: string }) {
try {
const query = `SELECT * FROM recipin.cmp_userfriendships WHERE id = $1`;
const friendship = await pool.query(query, [id]);
if (!friendship.rows.length) return { ok: false, code: 404, result: "Friendship with this code not found" };
if (!(friendship.rows[0].active) && friendship.rows[0].senderid == userid) {
return { ok: false, code: 403, result: "Please wait for friend request to be accepted" }
}
const statement = `
UPDATE recipin.cmp_userfriendships
SET active = $1,
@@ -154,10 +161,10 @@ export class User {
WHERE id = $4
RETURNING *;
`
const values = [data.active, data.pending, data.dateterminated || null, id];
const values = [data.active, data.pending, (data.dateterminated || null), id];
const result = await pool.query(statement, values);
if (result.rows.length) return result.rows[0];
return null;
if (result.rows.length) return { ok: true, code: 200, result: result.rows[0] }
return { ok: false, code: 400, result: "Bad request" }
} catch (e: any) {
throw new Error(e);
}

6
server/routes/comment.ts Normal file
View File

@@ -0,0 +1,6 @@
import { Express, Router } from "express"
const router = Router();
export const commentRoute = (app: Express) => {
app.use('/comment', router);
}

View File

@@ -49,9 +49,10 @@ export const friendRouter = (app: Express) => {
router.put('/:id', async (req, res, next) => {
const data = req.body;
const { id } = req.params;
const { user }: any = req.user;
try {
const result = await UserInstance.updateFriendship(id, data);
const result = await UserInstance.updateFriendship(id, user.id, data);
res.status(200).send(result);
} catch(e) {
next(e);

View File

@@ -1,4 +1,5 @@
import { Express, Router } from "express"
import { restrictAccess } from "../auth/middlewares";
import RecipeCtl from "../controllers/RecipeCtl";
const recipectl = new RecipeCtl();
@@ -18,6 +19,27 @@ export const recipeRoute = (app: Express) => {
}
})
router.get('/', async (req, res, next) => {
const { user }: any = req.user;
const { filterby } = req.query;
try {
let result;
switch (filterby) {
case "allaccessible":
result = await recipectl.getAllAccessible(user.id);
break;
default:
result = await recipectl.getAllAuthored(user.id);
break;
}
res.status(200).send(result);
} catch(e) {
next(e);
}
})
router.put('/:id', async (req, res, next) => {
const data = req.body;
const { id } = req.params;
@@ -30,12 +52,12 @@ export const recipeRoute = (app: Express) => {
}
})
router.post('/', async (req, res, next) => {
router.post('/', restrictAccess, async (req, res, next) => {
const { user }: any = req.user;
const data = req.body;
console.log(data);
try {
const result = await recipectl.post(data);
const result = await recipectl.post(user.id, data);
res.status(201).send(result);
} catch(e) {
next(e);

View File

@@ -31,21 +31,22 @@ export interface IRecipe extends HasHistory, CanDeactivate {
name: string
description?: string
preptime: string
authoruserid: IUser["id"]
authoruserid: string | number
}
export interface IIngredient extends HasHistory {
name: string
description?: string
createdbyid: string | number
}
export interface ICollection extends HasHistory, CanDeactivate {
name: string
ismaincollection: boolean
ownerid: IUser["id"]
ownerid: string | number
}
export interface IGroceryList extends HasHistory, CanDeactivate {
name: string
ownerid: IUser["id"]
ownerid: string | number
}