Skip to content

Commit

Permalink
Merge pull request #566 from ggwadera/db/prisma
Browse files Browse the repository at this point in the history
Start replacing Sequelize ORM with Prisma
  • Loading branch information
kondanna committed Mar 24, 2021
2 parents 3c03587 + daa76a0 commit 681225a
Show file tree
Hide file tree
Showing 12 changed files with 394 additions and 27 deletions.
6 changes: 2 additions & 4 deletions graphql/queryResolvers/lessons.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { prisma } from '../../prisma'
import { lessons } from './lessons'
import db from '../../helpers/dbload'

describe('Lessons resolver', () => {
const { Lesson } = db

test('lessons should return an empty array', async () => {
Lesson.findAll = jest.fn().mockReturnValue([])
prisma.lesson.findMany = jest.fn().mockReturnValue([])
expect(lessons()).toEqual([])
})
})
15 changes: 6 additions & 9 deletions graphql/queryResolvers/lessons.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import db from '../../helpers/dbload'

const { Lesson } = db
import { prisma } from '../../prisma'

export const lessons = () => {
return Lesson.findAll({
include: ['challenges'],
order: [
['order', 'ASC'],
['challenges', 'order', 'ASC']
]
return prisma.lesson.findMany({
include: { challenges: { orderBy: { order: 'asc' } } },
orderBy: {
order: 'asc'
}
})
}
21 changes: 12 additions & 9 deletions helpers/controllers/authController.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import bcrypt from 'bcrypt'
import db from '../dbload'
import { login, logout, signup, isTokenValid } from './authController'
import { chatSignUp } from '../mattermost'
import { prisma } from '../../prisma'

describe('auth controller', () => {
let userArgs
Expand All @@ -30,24 +31,26 @@ describe('auth controller', () => {
})

test('Login - should throw error if user cannot be found', async () => {
db.User.findOne = jest.fn().mockReturnValue(null)
prisma.user.findFirst = jest.fn().mockReturnValue(null)
return expect(
login({}, userArgs, { req: { session: {} } })
).rejects.toThrowError('User does not exist')
})

test('Login - should throw error if password is invalid', async () => {
db.User.findOne = jest.fn().mockReturnValue({})
prisma.user.findFirst = jest.fn().mockReturnValue({})
bcrypt.compare = jest.fn().mockReturnValue(false)
return expect(
login({}, userArgs, { req: { session: {} } })
).rejects.toThrowError('Password is invalid')
})

test('Login - should return success true if successful login', async () => {
db.User.findOne = jest
.fn()
.mockReturnValue({ username: 'testuser', cliToken: 'fakeCliToken' })
prisma.user.findFirst = jest.fn().mockReturnValue({
username: 'testuser',
password: 'fakepassword',
cliToken: 'fakeCliToken'
})
bcrypt.compare = jest.fn().mockReturnValue(true)
const result = await login({}, userArgs, { req: { session: {} } })
expect(result).toEqual({
Expand All @@ -58,10 +61,10 @@ describe('auth controller', () => {
})

test('Login - should return user with a new CLI token', async () => {
db.User.findOne = jest.fn().mockResolvedValue({
username: 'fakeUser',
update: obj => jest.fn().mockReturnThis(obj)
})
prisma.user.findFirst = jest
.fn()
.mockReturnValue({ username: 'fakeUser', password: 'fakePassword' })
prisma.user.update = obj => jest.fn().mockReturnThis(obj)
bcrypt.compare = jest.fn().mockReturnValue(true)
const result = await login({}, userArgs, { req: { session: {} } })
expect(result.cliToken).toBeTruthy()
Expand Down
18 changes: 15 additions & 3 deletions helpers/controllers/authController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { chatSignUp } from '../mattermost'
import { Context } from '../../@types/helpers'
import { encode, decode } from '../encoding'
import { sendSignupEmail } from '../mail'
import { prisma } from '../../prisma'

const { User } = db
const THREE_DAYS = 1000 * 60 * 60 * 24 * 3
Expand Down Expand Up @@ -34,17 +35,28 @@ export const login = async (_parent: void, arg: Login, ctx: Context) => {
throw new Error('Session Error')
}

const user = await User.findOne({ where: { username } })
const user = await prisma.user.findFirst({ where: { username } })
// TODO change username column to be unique
// const user = await prisma.user.findUnique({ where: { username } })
if (!user) {
throw new UserInputError('User does not exist')
}

const validLogin = await bcrypt.compare(password, user.password)
const validLogin = user.password
? await bcrypt.compare(password, user.password)
: false
if (!validLogin) {
throw new AuthenticationError('Password is invalid')
}

if (!user.cliToken) await user.update({ cliToken: nanoid() })
if (!user.cliToken) {
await prisma.user.update({
where: {
id: user.id
},
data: { cliToken: nanoid() }
})
}

const cliToken = { id: user.id, cliToken: user.cliToken }

Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
"build-storybook": "build-storybook -c .storybook -s ./public",
"test": "jest --coverage",
"type-check": "tsc",
"generate": "graphql-codegen --config codegen.yml"
"generate": "graphql-codegen --config codegen.yml",
"postinstall": "prisma generate"
},
"dependencies": {
"@apollo/client": "^3.3.11",
"@prisma/client": "2.19.0",
"@sentry/browser": "^5.30.0",
"@zeit/next-sass": "^1.0.1",
"apollo-server-micro": "^2.20.0",
Expand Down Expand Up @@ -94,6 +96,7 @@
"husky": "^4.3.7",
"jest": "^26.6.3",
"prettier": "^2.2.1",
"prisma": "2.19.0",
"react-test-renderer": "^17.0.1",
"sass-loader": "^10.0.1",
"svgo": "^1.3.2",
Expand All @@ -115,7 +118,8 @@
"coveragePathIgnorePatterns": [
"stories",
"graphql/index.tsx",
"<rootDir>/__tests__/utils/"
"<rootDir>/__tests__/utils/",
"prisma"
],
"testPathIgnorePatterns": [
"<rootDir>/__tests__/utils/"
Expand Down
28 changes: 28 additions & 0 deletions prisma/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { PrismaClient } from '@prisma/client'

declare global {
// eslint-disable-next-line no-var
var prismag: PrismaClient
}

const prismaOptions = {
datasources: {
db: {
url: `postgresql://${process.env.DB_USER}:${process.env.DB_PW}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`
}
}
}

// Avoid instantiating too many instances of Prisma in development
// https://www.prisma.io/docs/support/help-articles/nextjs-prisma-client-dev-practices#problem
export let prisma: PrismaClient

// check to use this workaround only in development and not in production
if (process.env.NODE_ENV === 'production') {
prisma = new PrismaClient(prismaOptions)
} else {
if (!global.prismag) {
global.prismag = new PrismaClient(prismaOptions)
}
prisma = global.prismag
}
144 changes: 144 additions & 0 deletions prisma/migrations/20210323001623_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
-- CreateTable
CREATE TABLE "Session" (
"sid" VARCHAR(36) NOT NULL,
"expires" TIMESTAMPTZ(6),
"data" TEXT,
"createdAt" TIMESTAMPTZ(6) NOT NULL,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,

PRIMARY KEY ("sid")
);

-- CreateTable
CREATE TABLE "alerts" (
"id" SERIAL NOT NULL,
"text" VARCHAR(255),
"type" VARCHAR(255),
"url" VARCHAR(255),
"urlCaption" VARCHAR(255),
"createdAt" TIMESTAMPTZ(6) NOT NULL,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,

PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "challenges" (
"id" SERIAL NOT NULL,
"status" VARCHAR(255),
"description" TEXT,
"title" VARCHAR(255),
"order" INTEGER,
"createdAt" TIMESTAMPTZ(6) NOT NULL,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"lessonId" INTEGER,

PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "lessons" (
"id" SERIAL NOT NULL,
"description" TEXT,
"docUrl" VARCHAR(255),
"githubUrl" VARCHAR(255),
"videoUrl" VARCHAR(255),
"order" INTEGER,
"title" VARCHAR(255),
"createdAt" TIMESTAMPTZ(6) NOT NULL,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"chatUrl" VARCHAR(255),

PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "stars" (
"id" SERIAL NOT NULL,
"lessonId" INTEGER,
"createdAt" TIMESTAMPTZ(6) NOT NULL,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"studentId" INTEGER,
"mentorId" INTEGER,
"comment" VARCHAR(255),

PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "submissions" (
"id" SERIAL NOT NULL,
"mrUrl" VARCHAR(255),
"diff" TEXT,
"comment" TEXT,
"status" VARCHAR(255),
"viewCount" INTEGER DEFAULT 0,
"createdAt" TIMESTAMPTZ(6) NOT NULL,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"userId" INTEGER,
"reviewerId" INTEGER,
"challengeId" INTEGER,
"lessonId" INTEGER,

PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "userLessons" (
"isPassed" VARCHAR(255),
"isTeaching" VARCHAR(255),
"isEnrolled" VARCHAR(255),
"createdAt" TIMESTAMPTZ(6) NOT NULL,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"lessonId" INTEGER NOT NULL,
"userId" INTEGER NOT NULL,

PRIMARY KEY ("lessonId","userId")
);

-- CreateTable
CREATE TABLE "users" (
"id" SERIAL NOT NULL,
"name" VARCHAR(255),
"username" VARCHAR(255),
"password" VARCHAR(255),
"email" VARCHAR(255),
"gsId" INTEGER,
"isOnline" BOOLEAN,
"createdAt" TIMESTAMPTZ(6) NOT NULL,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"isAdmin" VARCHAR(255) DEFAULT false,
"forgotToken" VARCHAR(255),
"cliToken" VARCHAR(255),
"emailVerificationToken" VARCHAR(255),
"tokenExpiration" TIMESTAMPTZ(6),

PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "challenges" ADD FOREIGN KEY ("lessonId") REFERENCES "lessons"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "stars" ADD FOREIGN KEY ("mentorId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "stars" ADD FOREIGN KEY ("studentId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "submissions" ADD FOREIGN KEY ("challengeId") REFERENCES "challenges"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "submissions" ADD FOREIGN KEY ("lessonId") REFERENCES "lessons"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "submissions" ADD FOREIGN KEY ("reviewerId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "submissions" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "userLessons" ADD FOREIGN KEY ("lessonId") REFERENCES "lessons"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "userLessons" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AddForeignKey
ALTER TABLE "stars" ADD FOREIGN KEY ("lessonId") REFERENCES "lessons"("id") ON DELETE SET NULL ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Warnings:
- The migration will add a unique constraint covering the columns `[username]` on the table `users`. If there are existing duplicate values, the migration will fail.
- Made the column `username` on table `users` required. The migration will fail if there are existing NULL values in that column.
*/
-- Delete duplicate usernames while keeping the latest entry
DELETE FROM "users" A USING "users" B
WHERE A.username = B.username AND A.id < B.id;

-- AlterTable
ALTER TABLE "users" ALTER COLUMN "username" SET NOT NULL;

-- CreateIndex
CREATE UNIQUE INDEX "users.username_unique" ON "users"("username");
3 changes: 3 additions & 0 deletions prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
Loading

1 comment on commit 681225a

@vercel
Copy link

@vercel vercel bot commented on 681225a Mar 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.