From 9bd1704da997a1c4889dc050424364b5738b82be Mon Sep 17 00:00:00 2001 From: Mikayla Dobson <93477693+innocuous-symmetry@users.noreply.github.com> Date: Sat, 26 Nov 2022 15:32:10 -0600 Subject: [PATCH] progress on friend route --- client/src/util/apiUtils.tsx | 4 +- server/controllers/UserCtl.ts | 30 +++++++++ server/db/populate.ts | 10 +-- .../sql/create/createcmp_userfriendships.sql | 1 + server/db/sql/derived/friendships.sql | 2 +- server/db/sql/get/friendshipbyid.sql | 25 ++++++++ server/models/user.ts | 62 ++++++++++++++++++- server/routes/friend.ts | 60 ++++++++++++++++++ server/routes/index.ts | 4 +- .../{subscriptions.ts => subscription.ts} | 0 10 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 server/db/sql/get/friendshipbyid.sql create mode 100644 server/routes/friend.ts rename server/routes/{subscriptions.ts => subscription.ts} (100%) diff --git a/client/src/util/apiUtils.tsx b/client/src/util/apiUtils.tsx index 34dbad3..49f25e5 100644 --- a/client/src/util/apiUtils.tsx +++ b/client/src/util/apiUtils.tsx @@ -67,11 +67,11 @@ export const attemptRegister = async (body: IUser) => { } // for user friendships -export const getFriendships = async (id: string | number) => { +export const getFriendships = async () => { try { const response = await axios({ method: "GET", - url: API + '/users/friends/' + id + url: API + '/friend' }) return Promise.resolve(response.data); diff --git a/server/controllers/UserCtl.ts b/server/controllers/UserCtl.ts index 82a6749..8fa07d6 100644 --- a/server/controllers/UserCtl.ts +++ b/server/controllers/UserCtl.ts @@ -53,4 +53,34 @@ export default class UserCtl { throw new Error(e); } } + + async getFriendshipByID(id: string, userid: string) { + try { + const { ok, code, result } = await UserInstance.getFriendshipByID(id, userid); + if (ok) return result; + throw createError(code, result); + } catch (e: any) { + throw new Error(e); + } + } + + async addFriendship(userid: string, targetid: string) { + try { + const result = await UserInstance.addFriendship(userid, targetid); + if (!result) throw createError(400, "something went wrong"); + return result; + } catch (e: any) { + throw new Error(e); + } + } + + async updateFriendship(id: 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; + } catch (e: any) { + throw new Error(e); + } + } } \ No newline at end of file diff --git a/server/db/populate.ts b/server/db/populate.ts index aae7103..82a1927 100644 --- a/server/db/populate.ts +++ b/server/db/populate.ts @@ -59,12 +59,12 @@ export default async function populate() { const populateFriendships = ` INSERT INTO recipin.cmp_userfriendships - (datecreated, active, firstuserid, seconduserid) + (datecreated, active, pending, firstuserid, seconduserid) VALUES - ($1, true, 1, 2), - ($1, true, 1, 4), - ($1, true, 2, 3), - ($1, true, 1, 3) + ($1, true, false, 1, 2), + ($1, true, false, 1, 4), + ($1, true, false, 2, 3), + ($1, true, false, 1, 3) ; ` diff --git a/server/db/sql/create/createcmp_userfriendships.sql b/server/db/sql/create/createcmp_userfriendships.sql index 73e8a49..8ee4e2c 100644 --- a/server/db/sql/create/createcmp_userfriendships.sql +++ b/server/db/sql/create/createcmp_userfriendships.sql @@ -2,6 +2,7 @@ CREATE TABLE IF NOT EXISTS recipin.cmp_userfriendships ( id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, datecreated varchar NOT NULL, active boolean NOT NULL, + pending boolean NOT NULL, dateterminated varchar, firstuserid int REFERENCES recipin.appusers (id), seconduserid int REFERENCES recipin.appusers (id) diff --git a/server/db/sql/derived/friendships.sql b/server/db/sql/derived/friendships.sql index 850df88..2dbd5ec 100644 --- a/server/db/sql/derived/friendships.sql +++ b/server/db/sql/derived/friendships.sql @@ -9,5 +9,5 @@ SELECT FROM recipin.cmp_userfriendships INNER JOIN recipin.appusers ON recipin.appusers.id = recipin.cmp_userfriendships.seconduserid -WHERE firstuserid = $1 +WHERE firstuserid = $1 OR seconduserid = $1 AND cmp_userfriendships.active = true; \ No newline at end of file diff --git a/server/db/sql/get/friendshipbyid.sql b/server/db/sql/get/friendshipbyid.sql new file mode 100644 index 0000000..8773af7 --- /dev/null +++ b/server/db/sql/get/friendshipbyid.sql @@ -0,0 +1,25 @@ +SELECT + recipin.cmp_userfriendships.id, + recipin.cmp_userfriendships.datecreated, + recipin.appusers.id, + recipin.appusers.firstname, + recipin.appusers.lastname, + recipin.appusers.handle, + recipin.appusers.email +FROM recipin.cmp_userfriendships +INNER JOIN recipin.appusers +ON recipin.appusers.id = recipin.cmp_userfriendships.firstuserid +WHERE recipin.cmp_userfriendships.id = $1 +UNION +SELECT + recipin.cmp_userfriendships.id, + recipin.cmp_userfriendships.datecreated, + recipin.appusers.id, + recipin.appusers.firstname, + recipin.appusers.lastname, + recipin.appusers.handle, + recipin.appusers.email +FROM recipin.cmp_userfriendships +INNER JOIN recipin.appusers +ON recipin.appusers.id = recipin.cmp_userfriendships.seconduserid +WHERE recipin.cmp_userfriendships.id = $1; \ No newline at end of file diff --git a/server/models/user.ts b/server/models/user.ts index e1dc5e0..4a82982 100644 --- a/server/models/user.ts +++ b/server/models/user.ts @@ -1,11 +1,9 @@ import { IUser } from "../schemas"; import fs from "fs"; -import pgPromise from "pg-promise"; import pool from '../db'; import now from "../util/now"; import { appRoot } from "../appRoot"; -const pgp = pgPromise({ capSQL: true }); export class User { async getAllUsers() { // behind auth @@ -104,4 +102,64 @@ export class User { throw new Error(e); } } + + async getFriendshipByID(id: string, userid: 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) { + 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 } + return { ok: false, code: 400, result: "Something went wrong" } + } + return { ok: true, code: 403, result: "Not authorized to access this resource" } + } + + return { ok: false, code: 404, result: "No friendship found with that ID" } + } catch (e: any) { + throw new Error(e); + } + } + + async addFriendship(userid: string, targetid: string) { + try { + const statement = ` + INSERT INTO recipin.cmp_userfriendships + (datecreated, active, pending, firstuserid, seconduserid) + VALUES ($1, false, true, $2, $3) + RETURNING *; + ` + const values = [now, userid, targetid]; + const result = await pool.query(statement, values); + if (result.rows.length) { + return result.rows[0]; + } + return null; + } catch (e: any) { + throw new Error(e); + } + } + + async updateFriendship(id: string, data: { active: boolean, pending: boolean, dateterminated?: string }) { + try { + const statement = ` + UPDATE recipin.cmp_userfriendships + SET active = $1, + pending = $2, + dateterminated = $3 + WHERE id = $4 + RETURNING *; + ` + 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; + } catch (e: any) { + throw new Error(e); + } + } } \ No newline at end of file diff --git a/server/routes/friend.ts b/server/routes/friend.ts new file mode 100644 index 0000000..cbe5739 --- /dev/null +++ b/server/routes/friend.ts @@ -0,0 +1,60 @@ +import { Express, Router } from 'express'; +import { restrictAccess } from '../auth/middlewares'; +import { UserCtl } from '../controllers'; + +const UserInstance = new UserCtl(); +const router = Router(); + +export const friendRouter = (app: Express) => { + app.use('/friend', router); + + router.post('/:targetid', restrictAccess, async (req, res, next) => { + const { user }: any = req.user; + const { targetid } = req.params; + + try { + const result = await UserInstance.addFriendship(user.id, targetid); + res.status(200).send(result); + } catch(e) { + next(e); + } + }) + + // get all friendships for a user + router.get('/', async (req, res, next) => { + const { user }: any = req.user; + + try { + const result = await UserInstance.getFriends(user.id); + res.status(200).send(result); + } catch(e) { + next(e); + } + }) + + // get one friendship by its id + router.get('/:id', async (req, res, next) => { + const { id } = req.params; + const { user }: any = req.user; + + try { + const result = await UserInstance.getFriendshipByID(id, user.id); + res.status(200).send(result); + } catch(e) { + next(e); + } + }) + + // update a friendship by its id + router.put('/:id', async (req, res, next) => { + const data = req.body; + const { id } = req.params; + + try { + const result = await UserInstance.updateFriendship(id, data); + res.status(200).send(result); + } catch(e) { + next(e); + } + }) +} \ No newline at end of file diff --git a/server/routes/index.ts b/server/routes/index.ts index 20d725f..1452a90 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -6,13 +6,15 @@ import { collectionRoute } from "./collection"; import { ingredientRoute } from "./ingredient"; import { groceryListRoute } from "./groceryList"; import { authRoute } from "./auth"; -import { subscriptionRoute } from "./subscriptions"; +import { subscriptionRoute } from "./subscription"; +import { friendRouter } from "./friend"; export const routes = async (app: Express, passport: PassportStatic) => { console.log('routes called'); authRoute(app, passport); userRoute(app); + friendRouter(app); recipeRoute(app); collectionRoute(app); subscriptionRoute(app); diff --git a/server/routes/subscriptions.ts b/server/routes/subscription.ts similarity index 100% rename from server/routes/subscriptions.ts rename to server/routes/subscription.ts