Skip to content

Commit e1bbf1c

Browse files
authored
Merge pull request #11 from GeneralMagicio/fix-schema-create-poll
fixed the schema and create create poll
2 parents 001ffdc + f8a79e5 commit e1bbf1c

File tree

11 files changed

+253
-28
lines changed

11 files changed

+253
-28
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
"dependencies": {
2323
"@nestjs/common": "^11.0.1",
2424
"@nestjs/core": "^11.0.1",
25+
"@nestjs/mapped-types": "*",
2526
"@nestjs/platform-express": "^11.0.1",
27+
"@prisma/client": "^6.5.0",
28+
"class-validator": "^0.14.1",
2629
"prisma": "^6.3.1",
2730
"reflect-metadata": "^0.2.2",
2831
"rxjs": "^7.8.1"

prisma/migrations/20250314191236_init/migration.sql renamed to prisma/migrations/20250322183108_changed_schema/migration.sql

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,31 @@ CREATE TYPE "ActionType" AS ENUM ('CREATED', 'VOTED');
33

44
-- CreateTable
55
CREATE TABLE "User" (
6+
"id" SERIAL NOT NULL,
67
"worldID" TEXT NOT NULL,
78
"name" TEXT NOT NULL,
89
"profilePicture" TEXT,
910
"pollsCreatedCount" INTEGER NOT NULL DEFAULT 0,
1011
"pollsParticipatedCount" INTEGER NOT NULL DEFAULT 0,
1112

12-
CONSTRAINT "User_pkey" PRIMARY KEY ("worldID")
13+
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
1314
);
1415

1516
-- CreateTable
1617
CREATE TABLE "UserAction" (
17-
"id" TEXT NOT NULL,
18-
"worldID" TEXT NOT NULL,
18+
"id" SERIAL NOT NULL,
19+
"userId" INTEGER NOT NULL,
1920
"actionID" TEXT NOT NULL,
20-
"pollID" TEXT NOT NULL,
21+
"pollId" INTEGER NOT NULL,
2122
"type" "ActionType" NOT NULL,
2223

2324
CONSTRAINT "UserAction_pkey" PRIMARY KEY ("id")
2425
);
2526

2627
-- CreateTable
2728
CREATE TABLE "Poll" (
28-
"pollID" TEXT NOT NULL,
29-
"authorUserID" TEXT NOT NULL,
29+
"pollId" SERIAL NOT NULL,
30+
"authorUserId" INTEGER NOT NULL,
3031
"title" TEXT NOT NULL,
3132
"description" TEXT,
3233
"options" TEXT[],
@@ -38,32 +39,38 @@ CREATE TABLE "Poll" (
3839
"participantCount" INTEGER NOT NULL DEFAULT 0,
3940
"voteResults" JSONB NOT NULL,
4041

41-
CONSTRAINT "Poll_pkey" PRIMARY KEY ("pollID")
42+
CONSTRAINT "Poll_pkey" PRIMARY KEY ("pollId")
4243
);
4344

4445
-- CreateTable
4546
CREATE TABLE "Vote" (
4647
"voteID" TEXT NOT NULL,
47-
"worldID" TEXT NOT NULL,
48-
"pollID" TEXT NOT NULL,
48+
"userId" INTEGER NOT NULL,
49+
"pollId" INTEGER NOT NULL,
4950
"votingPower" INTEGER NOT NULL,
5051
"weightDistribution" JSONB NOT NULL,
5152
"proof" TEXT NOT NULL,
5253

5354
CONSTRAINT "Vote_pkey" PRIMARY KEY ("voteID")
5455
);
5556

57+
-- CreateIndex
58+
CREATE UNIQUE INDEX "User_worldID_key" ON "User"("worldID");
59+
5660
-- CreateIndex
5761
CREATE UNIQUE INDEX "UserAction_actionID_key" ON "UserAction"("actionID");
5862

5963
-- AddForeignKey
60-
ALTER TABLE "UserAction" ADD CONSTRAINT "UserAction_worldID_fkey" FOREIGN KEY ("worldID") REFERENCES "User"("worldID") ON DELETE CASCADE ON UPDATE CASCADE;
64+
ALTER TABLE "UserAction" ADD CONSTRAINT "UserAction_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
65+
66+
-- AddForeignKey
67+
ALTER TABLE "UserAction" ADD CONSTRAINT "UserAction_pollId_fkey" FOREIGN KEY ("pollId") REFERENCES "Poll"("pollId") ON DELETE RESTRICT ON UPDATE CASCADE;
6168

6269
-- AddForeignKey
63-
ALTER TABLE "Poll" ADD CONSTRAINT "Poll_authorUserID_fkey" FOREIGN KEY ("authorUserID") REFERENCES "User"("worldID") ON DELETE CASCADE ON UPDATE CASCADE;
70+
ALTER TABLE "Poll" ADD CONSTRAINT "Poll_authorUserId_fkey" FOREIGN KEY ("authorUserId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
6471

6572
-- AddForeignKey
66-
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_worldID_fkey" FOREIGN KEY ("worldID") REFERENCES "User"("worldID") ON DELETE CASCADE ON UPDATE CASCADE;
73+
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
6774

6875
-- AddForeignKey
69-
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_pollID_fkey" FOREIGN KEY ("pollID") REFERENCES "Poll"("pollID") ON DELETE CASCADE ON UPDATE CASCADE;
76+
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_pollId_fkey" FOREIGN KEY ("pollId") REFERENCES "Poll"("pollId") ON DELETE RESTRICT ON UPDATE CASCADE;

prisma/schema.prisma

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ datasource db {
1515

1616

1717
model User {
18-
worldID String @id
18+
id Int @id @default(autoincrement())
19+
worldID String @unique
1920
name String
2021
profilePicture String?
2122
pollsCreatedCount Int @default(0)
@@ -26,40 +27,43 @@ model User {
2627
}
2728

2829
model UserAction {
29-
id String @id @default(uuid())
30-
worldID String
30+
id Int @id @default(autoincrement())
31+
userId Int
3132
actionID String @unique
32-
pollID String
33+
pollId Int
3334
type ActionType
34-
user User @relation(fields: [worldID], references: [worldID], onDelete: Cascade)
35+
user User @relation(fields: [userId], references: [id])
36+
poll Poll @relation(fields: [pollId], references: [pollId])
37+
3538
}
3639

3740
model Poll {
38-
pollID String @id @default(uuid())
39-
authorUserID String
41+
pollId Int @id @default(autoincrement())
42+
authorUserId Int
4043
title String
4144
description String?
4245
options String[]
4346
creationDate DateTime @default(now())
4447
startDate DateTime
45-
endDate DateTime
48+
endDate DateTime
4649
tags String[]
4750
isAnonymous Boolean @default(false)
4851
participantCount Int @default(0)
4952
voteResults Json
50-
author User @relation("PollAuthor", fields: [authorUserID], references: [worldID], onDelete: Cascade)
53+
author User @relation("PollAuthor", fields: [authorUserId], references: [id])
5154
votes Vote[]
55+
userAction UserAction[]
5256
}
5357

5458
model Vote {
5559
voteID String @id @default(uuid())
56-
worldID String
57-
pollID String
60+
userId Int
61+
pollId Int
5862
votingPower Int
5963
weightDistribution Json
6064
proof String
61-
user User @relation(fields: [worldID], references: [worldID], onDelete: Cascade)
62-
poll Poll @relation(fields: [pollID], references: [pollID], onDelete: Cascade)
65+
user User @relation(fields: [userId], references: [id])
66+
poll Poll @relation(fields: [pollId], references: [pollId])
6367
}
6468

6569
enum ActionType {

src/app.module.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { Module } from '@nestjs/common';
22
import { AppController } from './app.controller';
33
import { AppService } from './app.service';
4+
import { DatabaseService } from './database/database.service';
5+
import { DatabaseModule } from './database/database.module';
6+
import { PollModule } from './poll/poll.module';
47

58
@Module({
6-
imports: [],
9+
imports: [DatabaseModule, PollModule],
710
controllers: [AppController],
8-
providers: [AppService],
11+
providers: [AppService, DatabaseService],
912
})
1013
export class AppModule {}

src/database/database.module.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Global, Module } from '@nestjs/common';
2+
import { DatabaseService } from './database.service';
3+
4+
@Global()
5+
@Module({
6+
providers: [DatabaseService],
7+
exports: [DatabaseService],
8+
})
9+
export class DatabaseModule {}

src/database/database.service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Injectable, OnModuleInit } from '@nestjs/common';
2+
import { PrismaClient } from '@prisma/client';
3+
4+
@Injectable()
5+
export class DatabaseService extends PrismaClient implements OnModuleInit {
6+
async onModuleInit() {
7+
await this.$connect()
8+
.then(() => console.log('Connected to DB'))
9+
.catch((err) => console.log('Error connecting to DB', err));
10+
}
11+
}

src/poll/createPoll.dto.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {
2+
IsArray,
3+
IsBoolean,
4+
IsDateString,
5+
IsNotEmpty,
6+
IsOptional,
7+
IsString,
8+
} from 'class-validator';
9+
10+
export class CreatePollDto {
11+
@IsString()
12+
@IsNotEmpty()
13+
title: string;
14+
15+
@IsString()
16+
@IsOptional()
17+
description?: string;
18+
19+
@IsArray()
20+
@IsString({ each: true })
21+
@IsNotEmpty()
22+
options: string[];
23+
24+
@IsDateString()
25+
@IsNotEmpty()
26+
startDate: string;
27+
28+
@IsDateString()
29+
@IsNotEmpty()
30+
endDate: string;
31+
32+
@IsArray()
33+
@IsString({ each: true })
34+
@IsOptional()
35+
tags?: string[];
36+
37+
@IsBoolean()
38+
@IsOptional()
39+
isAnonymous?: boolean;
40+
}

src/poll/poll.controller.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Controller, Get, Post, Body, Param, Delete } from '@nestjs/common';
2+
import { PollService } from './poll.service';
3+
import { Prisma } from '@prisma/client';
4+
import { CreatePollDto } from './createPoll.dto';
5+
6+
@Controller('poll')
7+
export class PollController {
8+
constructor(private readonly pollService: PollService) {}
9+
10+
@Post()
11+
create(@Body() createPollDto: CreatePollDto) {
12+
let userId = 1; // need to implement Auth
13+
return this.pollService.createPoll(userId, createPollDto);
14+
}
15+
16+
@Get()
17+
getPolls() {
18+
return this.pollService.getPolls();
19+
}
20+
21+
@Get(':id')
22+
getPollDetails(@Param('id') id: string) {
23+
return this.pollService.getPollDetails(Number(id));
24+
}
25+
}

src/poll/poll.module.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Module } from '@nestjs/common';
2+
import { PollService } from './poll.service';
3+
import { PollController } from './poll.controller';
4+
5+
@Module({
6+
controllers: [PollController],
7+
providers: [PollService],
8+
})
9+
export class PollModule {}

src/poll/poll.service.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { BadRequestException, Injectable } from '@nestjs/common';
2+
import { ActionType, Prisma } from '@prisma/client';
3+
import { DatabaseService } from 'src/database/database.service';
4+
import { CreatePollDto } from './createPoll.dto';
5+
6+
@Injectable()
7+
export class PollService {
8+
constructor(private readonly databaseService: DatabaseService) {}
9+
10+
async createPoll(userId: number, createPollDto: CreatePollDto) {
11+
const user = await this.databaseService.user.findUnique({
12+
where: { id: userId },
13+
});
14+
if (!user) {
15+
throw new BadRequestException('User does not exist');
16+
}
17+
const startDate = new Date(createPollDto.startDate);
18+
const endDate = new Date(createPollDto.endDate);
19+
const now = new Date();
20+
21+
if (startDate < now) {
22+
throw new BadRequestException('Start date cannot be in the past');
23+
}
24+
25+
if (endDate <= startDate) {
26+
throw new BadRequestException('End date must be after start date');
27+
}
28+
29+
return this.databaseService.$transaction(async (tx) => {
30+
// Create the poll
31+
const newPoll = await tx.poll.create({
32+
data: {
33+
authorUserId: userId,
34+
title: createPollDto.title,
35+
description: createPollDto.description,
36+
options: createPollDto.options,
37+
startDate,
38+
endDate,
39+
tags: createPollDto.tags || [],
40+
isAnonymous: createPollDto.isAnonymous || false,
41+
voteResults: {}, // Initialize empty vote results
42+
},
43+
});
44+
45+
// Create user action for CREATED
46+
await tx.userAction.create({
47+
data: {
48+
userId,
49+
actionID: `created-${newPoll.pollId}-${userId}-${Date.now()}`,
50+
pollId: newPoll.pollId,
51+
type: ActionType.CREATED,
52+
},
53+
});
54+
55+
// Update user's pollsCreatedCount
56+
await tx.user.update({
57+
where: { id: userId },
58+
data: {
59+
pollsCreatedCount: {
60+
increment: 1,
61+
},
62+
},
63+
});
64+
65+
return newPoll;
66+
});
67+
}
68+
69+
async getPolls() {
70+
return this.databaseService.poll.findMany({});
71+
}
72+
73+
async getPollDetails(id: number) {
74+
return this.databaseService.poll.findUnique({
75+
where: {
76+
pollId: id,
77+
},
78+
});
79+
}
80+
}

0 commit comments

Comments
 (0)