Skip to content

Commit

Permalink
Merge pull request #59 from jaiminRaiyani/feat/hashtag-management
Browse files Browse the repository at this point in the history
Feat/hashtag management
  • Loading branch information
addegbenga authored Feb 10, 2025
2 parents a5d304c + 380903d commit cb78668
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 2 deletions.
Empty file.
11 changes: 11 additions & 0 deletions backend/prisma/migrations/20250210093710_hashtags/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- CreateTable
CREATE TABLE "hashtags" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "hashtags_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "hashtags_name_key" ON "hashtags"("name");
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "hashtags" ADD COLUMN "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
8 changes: 8 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,11 @@ enum Role {
Admin
}

model Hashtag {
id String @id @default(uuid())
name String @unique
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @map("updated_at")
@@map("hashtags")
}
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import { AllExceptionsFilter } from './common/exceptions/all-exception.filter';
import { APP_FILTER, APP_PIPE } from '@nestjs/core';
import { CategoryModule } from './category/category.module';
import { WagerModule } from './wager/wager.module';
import { HashtagsModule } from './hashtags/hashtags.module';

@Module({
imports: [
UsersModule,
AuthModule,
CategoryModule,
WagerModule,
HashtagsModule,
ConfigModule.forRoot({
isGlobal: true,
cache: true,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/common/guards/auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class AuthGuard implements CanActivate {
constructor(
private jwtService: JwtService,
private reflector: Reflector,
) {}
) { }

async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
Expand Down
2 changes: 1 addition & 1 deletion backend/src/common/guards/roles.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ROLES_KEY } from '../decorators/roles.decorator';

@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
constructor(private reflector: Reflector) { }

canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
Expand Down
10 changes: 10 additions & 0 deletions backend/src/hashtags/dto/create-hashtag.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IsString, IsNotEmpty, Matches } from 'class-validator';

export class CreateHashtagDto {
@IsString()
@IsNotEmpty()
@Matches(/^[a-zA-Z0-9_]+$/, {
message: 'Hashtag name can only contain letters, numbers, and underscores',
})
name: string;
}
24 changes: 24 additions & 0 deletions backend/src/hashtags/hashtags.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Controller, Post, Get, Body, UseGuards } from '@nestjs/common';
import { HashtagsService } from './hashtags.service';
import { RolesGuard } from '../common/guards/roles.guard'; // Use existing RolesGuard
import { Roles } from '../common/decorators/roles.decorator'; // Apply role-based access
import { Role } from '../common/enums/roles.enum'; // Ensure correct role names
import { CreateHashtagDto } from './dto/create-hashtag.dto';

@Controller('hashtags')
@UseGuards(RolesGuard) // Apply role guard globally in this controller
export class HashtagsController {
constructor(private readonly hashtagsService: HashtagsService) {}

@Post()
@Roles(Role.Admin)
async create(@Body() createHashtagDto: CreateHashtagDto) {
const hashtag = await this.hashtagsService.create(createHashtagDto);
return { message: 'Hashtag created successfully', data: hashtag };
}

@Get()
async findAll() {
return this.hashtagsService.findAll();
}
}
10 changes: 10 additions & 0 deletions backend/src/hashtags/hashtags.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { HashtagsService } from './hashtags.service';
import { HashtagsController } from './hashtags.controller';
import { PrismaService } from '../prisma/prisma.service';

@Module({
controllers: [HashtagsController],
providers: [HashtagsService, PrismaService],
})
export class HashtagsModule {}
35 changes: 35 additions & 0 deletions backend/src/hashtags/hashtags.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HashtagsService } from './hashtags.service';
import { PrismaService } from 'nestjs-prisma';

describe('HashtagsService', () => {
let service: HashtagsService;
let prisma: PrismaService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [HashtagsService, PrismaService],
}).compile();

service = module.get<HashtagsService>(HashtagsService);
prisma = module.get<PrismaService>(PrismaService);
});

it('should create a hashtag', async () => {
jest
.spyOn(prisma.hashtag, 'create')
.mockResolvedValue({ id: '1', name: 'NestJS', createdAt: new Date() });

const result = await service.create({ name: 'NestJS' });
expect(result.name).toBe('NestJS');
});

it('should find all hashtags', async () => {
jest
.spyOn(prisma.hashtag, 'findMany')
.mockResolvedValue([{ id: '1', name: 'NestJS', createdAt: new Date() }]);

const result = await service.findAll();
expect(result.length).toBe(1);
});
});
18 changes: 18 additions & 0 deletions backend/src/hashtags/hashtags.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateHashtagDto } from './dto/create-hashtag.dto';

@Injectable()
export class HashtagsService {
constructor(private prisma: PrismaService) {}

async create(createHashtagDto: CreateHashtagDto) {
const hashtag = await this.prisma.hashtag.create({
data: createHashtagDto,
});
return hashtag;
}
async findAll() {
return this.prisma.hashtag.findMany();
}
}

0 comments on commit cb78668

Please sign in to comment.