Skip to content

Commit

Permalink
added basic crud for application periods
Browse files Browse the repository at this point in the history
  • Loading branch information
FearsomeRover committed Aug 13, 2024
1 parent 23ec5fd commit fdb816f
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 1 deletion.
1 change: 1 addition & 0 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.3.8",
"@prisma/client": "^5.13.0",
"class-validator": "^0.14.1",
"nestjs-prisma": "^0.23.0",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
Expand Down
3 changes: 2 additions & 1 deletion apps/backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { PrismaModule } from 'nestjs-prisma';

import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ApplicationPeriodModule } from './application-period/application-period.module';
import { AuthModule } from './auth/auth.module';
import { UserModule } from './user/user.module';

@Module({
imports: [PrismaModule.forRoot({ isGlobal: true }), UserModule, AuthModule],
imports: [PrismaModule.forRoot({ isGlobal: true }), UserModule, AuthModule, ApplicationPeriodModule],
controllers: [AppController],
providers: [AppService],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { CurrentUser } from '@kir-dev/passport-authsch';
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth } from '@nestjs/swagger';
import { ApplicationPeriod, Role, User } from '@prisma/client';
import { Roles } from 'src/auth/decorators/Roles.decorator';
import { RolesGuard } from 'src/auth/roles.guard';

import { ApplicationPeriodService } from './application-period.service';
import { CreateApplicationPeriodDto } from './dto/create-application-period.dto';
import { GetApplicationPeriodsDto } from './dto/get-application-periods.dto';
import { SimpleApplicationPeriodDto } from './dto/simple-application-period.dto';
import { UpdateApplicationPeriodDto } from './dto/update-application-period.dto';

@Controller('application-periods')
export class ApplicationPeriodController {
constructor(private readonly applicationPeriodService: ApplicationPeriodService) {}

@Get()
async findAll(@Query() getApplicationPeriodsDto: GetApplicationPeriodsDto): Promise<SimpleApplicationPeriodDto[]> {
return this.applicationPeriodService.findAll(getApplicationPeriodsDto);
}

@Get('current')
async getCurrentPeriod(): Promise<ApplicationPeriod> {
return this.applicationPeriodService.getCurrentPeriod();
}

@Get(':id')
async findOne(@Param('id') id: string): Promise<ApplicationPeriod> {
return this.applicationPeriodService.findOne(Number(id));
}

@UseGuards(AuthGuard('jwt'), RolesGuard)
@ApiBearerAuth()
@Roles(Role.BODY_ADMIN, Role.BODY_MEMBER)
@Post()
async create(
@Body() createApplicationPeriodDto: CreateApplicationPeriodDto,
@CurrentUser() user: User
): Promise<ApplicationPeriod> {
return this.applicationPeriodService.create(createApplicationPeriodDto, user);
}

@UseGuards(AuthGuard('jwt'), RolesGuard)
@ApiBearerAuth()
@Roles(Role.BODY_ADMIN, Role.BODY_MEMBER)
@Delete(':id')
async delete(@Param('id') id: string): Promise<ApplicationPeriod> {
return this.applicationPeriodService.delete(Number(id));
}

@UseGuards(AuthGuard('jwt'), RolesGuard)
@ApiBearerAuth()
@Roles(Role.BODY_ADMIN, Role.BODY_MEMBER)
@Patch(':id')
async update(
@Body() updateApplicationPeriodDto: UpdateApplicationPeriodDto,
@Param() id: string
): Promise<ApplicationPeriod> {
return this.applicationPeriodService.update(updateApplicationPeriodDto, Number(id));
}
}
10 changes: 10 additions & 0 deletions apps/backend/src/application-period/application-period.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';

import { ApplicationPeriodController } from './application-period.controller';
import { ApplicationPeriodService } from './application-period.service';

@Module({
controllers: [ApplicationPeriodController],
providers: [ApplicationPeriodService],
})
export class ApplicationPeriodModule {}
110 changes: 110 additions & 0 deletions apps/backend/src/application-period/application-period.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { ApplicationPeriod } from '@prisma/client';
import { PrismaService } from 'nestjs-prisma';

import { CreateApplicationPeriodDto } from './dto/create-application-period.dto';
import { GetApplicationPeriodsDto } from './dto/get-application-periods.dto';
import { UpdateApplicationPeriodDto } from './dto/update-application-period.dto';

@Injectable()
export class ApplicationPeriodService {
constructor(private readonly prisma: PrismaService) {}
findAll(getApplicationPeriodsDto: GetApplicationPeriodsDto) {
const skip = getApplicationPeriodsDto.page * getApplicationPeriodsDto.page_size;
return this.prisma.applicationPeriod.findMany({
skip,
take: Number(getApplicationPeriodsDto.page_size),
});
}
async getCurrentPeriod(): Promise<ApplicationPeriod> {
const period = await this.prisma.applicationPeriod.findFirst({
where: {
applicationPeriodStartAt: {
lte: new Date(),
},
applicationPeriodEndAt: {
gte: new Date(),
},
},
});
if (!period) {
throw new Error('No current application period found');
}
return period;
}

async findOne(id: number): Promise<ApplicationPeriod> {
const period = await this.prisma.applicationPeriod.findUnique({
where: {
id,
},
});
if (!period) {
throw new BadRequestException('Application period not found');
}
return period;
}

async create(createApplicationPeriodDto: CreateApplicationPeriodDto, user): Promise<ApplicationPeriod> {
const periods = await this.prisma.applicationPeriod.findMany();
const newPeriodStart = new Date(createApplicationPeriodDto.applicationPeriodStartAt);
const newPeriodEnd = new Date(createApplicationPeriodDto.applicationPeriodEndAt);
periods.forEach((period) => {
const start = new Date(period.applicationPeriodStartAt);
const end = new Date(period.applicationPeriodEndAt);
if (start <= newPeriodEnd && end >= newPeriodStart) {
throw new BadRequestException('Application period overlaps with another period');
}
});
return this.prisma.applicationPeriod.create({
data: {
...createApplicationPeriodDto,
author: {
connect: {
authSchId: user.authSchId,
},
},
},
});
}

async delete(id: number): Promise<ApplicationPeriod> {
try {
return await this.prisma.applicationPeriod.delete({
where: {
id,
},
});
} catch (e) {
throw new BadRequestException('Application does not exist');
}
}

async update(updateApplicationPeriodDto: UpdateApplicationPeriodDto, id: number): Promise<ApplicationPeriod> {
const periods = await this.prisma.applicationPeriod.findMany();
if (
Boolean(updateApplicationPeriodDto.applicationPeriodStartAt) ||
Boolean(updateApplicationPeriodDto.applicationPeriodEndAt)
) {
const newPeriodStart = new Date(updateApplicationPeriodDto.applicationPeriodStartAt);
const newPeriodEnd = new Date(updateApplicationPeriodDto.applicationPeriodEndAt);
periods.forEach((period) => {
const start = new Date(period.applicationPeriodStartAt);
const end = new Date(period.applicationPeriodEndAt);
if (period.id !== id && start <= newPeriodEnd && end >= newPeriodStart) {
throw new BadRequestException('Application period overlaps with another period');
}
});
}
try {
return this.prisma.applicationPeriod.update({
where: {
id,
},
data: updateApplicationPeriodDto,
});
} catch (e) {
throw new BadRequestException('Application does not exist');
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class CreateApplicationPeriodDto {
name: string;
applicationPeriodStartAt: Date;
applicationPeriodEndAt: Date;
ticketsAreValid: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { IsInt, IsOptional, Min } from 'class-validator';
export class GetApplicationPeriodsDto {
@IsOptional()
@IsInt()
@Min(0)
page: number;
@IsOptional()
@IsInt()
@Min(1)
page_size: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class SimpleApplicationPeriodDto {
id: number;
name: string;
applicationPeriodStartAt: Date;
applicationPeriodEndAt: Date;
createdAt: Date;
updatedAt: Date;
ticketsAreValid: boolean;
authorId: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PartialType } from '@nestjs/swagger';

import { CreateApplicationPeriodDto } from './create-application-period.dto';

export class UpdateApplicationPeriodDto extends PartialType(CreateApplicationPeriodDto) {}
24 changes: 24 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,11 @@
"@types/node" "*"
"@types/send" "*"

"@types/validator@^13.11.8":
version "13.12.0"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.12.0.tgz#1fe4c3ae9de5cf5193ce64717c99ef2fa7d8756f"
integrity sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==

"@typescript-eslint/eslint-plugin@^7.7.1":
version "7.7.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz#50a9044e3e5fe76b22caf64fb7fc1f97614bdbfd"
Expand Down Expand Up @@ -1429,6 +1434,15 @@ chrome-trace-event@^1.0.2:
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==

class-validator@^0.14.1:
version "0.14.1"
resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110"
integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==
dependencies:
"@types/validator" "^13.11.8"
libphonenumber-js "^1.10.53"
validator "^13.9.0"

class-variance-authority@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522"
Expand Down Expand Up @@ -3176,6 +3190,11 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"

libphonenumber-js@^1.10.53:
version "1.11.5"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.5.tgz#50a441da5ff9ed9a322d796a14f1e9cbc0fdabdf"
integrity sha512-TwHR5BZxGRODtAfz03szucAkjT5OArXr+94SMtAM2pYXIlQNVMrxvb6uSCbnaJJV6QXEyICk7+l6QPgn72WHhg==

lilconfig@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
Expand Down Expand Up @@ -4915,6 +4934,11 @@ v8-compile-cache-lib@^3.0.1:
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==

validator@^13.9.0:
version "13.12.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f"
integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==

vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
Expand Down

0 comments on commit fdb816f

Please sign in to comment.