From 24e245fc6fa9ecdd6a54536a4ecaf893b4e1728f Mon Sep 17 00:00:00 2001 From: Aaron William Po Date: Sun, 12 Nov 2023 17:32:43 -0500 Subject: [PATCH] feat: implement user follow system --- .../migrations/20231112221155_/migration.sql | 14 +++++ src/prisma/schema.prisma | 12 +++++ .../seed/create/createNewUserFollows.ts | 54 +++++++++++++++++++ src/prisma/seed/create/createNewUsers.ts | 3 ++ src/prisma/seed/index.ts | 5 ++ 5 files changed, 88 insertions(+) create mode 100644 src/prisma/migrations/20231112221155_/migration.sql create mode 100644 src/prisma/seed/create/createNewUserFollows.ts diff --git a/src/prisma/migrations/20231112221155_/migration.sql b/src/prisma/migrations/20231112221155_/migration.sql new file mode 100644 index 0000000..ade0a10 --- /dev/null +++ b/src/prisma/migrations/20231112221155_/migration.sql @@ -0,0 +1,14 @@ +-- CreateTable +CREATE TABLE "UserFollow" ( + "followerId" TEXT NOT NULL, + "followingId" TEXT NOT NULL, + "followedAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserFollow_pkey" PRIMARY KEY ("followerId","followingId") +); + +-- AddForeignKey +ALTER TABLE "UserFollow" ADD CONSTRAINT "UserFollow_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserFollow" ADD CONSTRAINT "UserFollow_followingId_fkey" FOREIGN KEY ("followingId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/src/prisma/schema.prisma b/src/prisma/schema.prisma index e485041..42b7ec6 100644 --- a/src/prisma/schema.prisma +++ b/src/prisma/schema.prisma @@ -44,6 +44,18 @@ model User { beerStyleLikes BeerStyleLike[] beerStyleComments BeerStyleComment[] userAvatar UserAvatar? + followedBy UserFollow[] @relation("following") + following UserFollow[] @relation("follower") +} + +model UserFollow { + follower User @relation("follower", fields: [followerId], references: [id]) + followerId String + following User @relation("following", fields: [followingId], references: [id]) + followingId String + followedAt DateTime @default(now()) @db.Timestamptz(3) + + @@id([followerId, followingId]) } model UserAvatar { diff --git a/src/prisma/seed/create/createNewUserFollows.ts b/src/prisma/seed/create/createNewUserFollows.ts new file mode 100644 index 0000000..aa87f0b --- /dev/null +++ b/src/prisma/seed/create/createNewUserFollows.ts @@ -0,0 +1,54 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { faker } from '@faker-js/faker'; +import type { User } from '@prisma/client'; + +import DBClient from '../../DBClient'; + +interface CreateNewUserFollowsArgs { + joinData: { users: User[] }; +} + +interface UserFollowData { + followerId: string; + followingId: string; + followedAt: Date; +} + +const createNewUserFollows = async ({ + joinData: { users }, +}: CreateNewUserFollowsArgs) => { + const userFollows: UserFollowData[] = []; + + users.forEach((user) => { + // Get 20 random users to follow. + const randomUsers = users + .filter((randomUser) => randomUser.id !== user.id) + .sort(() => Math.random() - Math.random()) + .slice(0, 20); + + // Get the user to follow the random users, and the random users to follow the user. + const data = randomUsers.flatMap((randomUser) => [ + { + followerId: user.id, + followingId: randomUser.id, + followedAt: faker.date.between({ from: user.createdAt, to: new Date() }), + }, + { + followerId: randomUser.id, + followingId: user.id, + followedAt: faker.date.between({ from: randomUser.createdAt, to: new Date() }), + }, + ]); + + userFollows.push(...data); + }); + + await DBClient.instance.userFollow.createMany({ + data: userFollows, + skipDuplicates: true, + }); + + return DBClient.instance.userFollow.findMany(); +}; + +export default createNewUserFollows; diff --git a/src/prisma/seed/create/createNewUsers.ts b/src/prisma/seed/create/createNewUsers.ts index e61fcf6..96f6a2f 100644 --- a/src/prisma/seed/create/createNewUsers.ts +++ b/src/prisma/seed/create/createNewUsers.ts @@ -18,6 +18,7 @@ interface UserData { hash: string; accountIsVerified: boolean; role: 'USER' | 'ADMIN'; + bio: string; } const createNewUsers = async ({ numberOfUsers }: CreateNewUsersArgs) => { @@ -54,6 +55,7 @@ const createNewUsers = async ({ numberOfUsers }: CreateNewUsersArgs) => { const dateOfBirth = faker.date.birthdate({ mode: 'age', min: 19 }); const createdAt = faker.date.past({ years: 4 }); + const bio = faker.lorem.paragraphs(3).replace(/\n/g, ' '); const user: UserData = { firstName, @@ -63,6 +65,7 @@ const createNewUsers = async ({ numberOfUsers }: CreateNewUsersArgs) => { dateOfBirth, createdAt, hash, + bio, accountIsVerified: true, role: 'USER', }; diff --git a/src/prisma/seed/index.ts b/src/prisma/seed/index.ts index 4139ba2..e4d4420 100644 --- a/src/prisma/seed/index.ts +++ b/src/prisma/seed/index.ts @@ -19,6 +19,7 @@ import createAdminUser from './create/createAdminUser'; import createNewBeerStyleComments from './create/createNewBeerStyleComments'; import createNewBeerStyleLikes from './create/createNewBeerStyleLikes'; import createNewUserAvatars from './create/createNewUserAvatars'; +import createNewUserFollows from './create/createNewUserFollows'; (async () => { try { @@ -37,6 +38,9 @@ import createNewUserAvatars from './create/createNewUserAvatars'; const userAvatars = await createNewUserAvatars({ joinData: { users } }); logger.info('User avatars created successfully.'); + const userFollows = await createNewUserFollows({ joinData: { users } }); + logger.info('User follows created successfully.'); + const locations = await createNewLocations({ numberOfLocations: 500, joinData: { users }, @@ -108,6 +112,7 @@ import createNewUserAvatars from './create/createNewUserAvatars'; logger.info({ numberOfUsers: users.length, numberOfUserAvatars: userAvatars.length, + numberOfUserFollows: userFollows.length, numberOfBreweryPosts: breweryPosts.length, numberOfBeerPosts: beerPosts.length, numberOfBeerStyles: beerStyles.length,