Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: group, codes controllers and services #7

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ module.exports = {
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'on',
'@typescript-eslint/explicit-function-return-type': 2,
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'on',
'@typescript-eslint/no-explicit-any': 2,
},
};
3,522 changes: 1,263 additions & 2,259 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@
"check-branch-name": "chmod 755 ./.husky/hooks/check-branch-naming.sh && sh ./.husky/hooks/check-branch-naming.sh"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/common": "^10.3.10",
"@nestjs/core": "^10.3.10",
"@nestjs/mongoose": "^10.0.10",
"@nestjs/platform-express": "^10.3.10",
"@types/uuid": "^10.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"commitlint": "^19.3.0",
"mongoose": "^8.5.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
"rxjs": "^7.8.1",
"uuid": "^10.0.0"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
Expand Down
58 changes: 58 additions & 0 deletions src/accounts/dtos/account.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Optional } from '@nestjs/common';
import { Expose, Type } from 'class-transformer';
import {
IsArray,
IsDefined,
IsNotEmpty,
IsString,
MaxLength,
} from 'class-validator';
import { GroupDto } from 'src/groups/dtos/group.dto';
import {
AccountBasicDto,
FieldConstraints,
SystemStatus,
} from 'src/libs/shared';
import { PermissionRule, RoleDto } from 'src/roles/dtos/role.dto';

export class UserAccountDto extends AccountBasicDto {
get fullName(): string | null {
if (this.firstName && this.lastName) {
return `${this.firstName[0].toUpperCase()}${this.lastName[0].toUpperCase()}`;
}
return null;
}

get initials(): string {
if (this.firstName && this.lastName) {
return `${this.firstName[0].toUpperCase()}${this.lastName[0].toUpperCase()}`;
} else if (this.firstName) {
return `${this.firstName[0].toUpperCase()}`;
} else if (this.lastName) {
return `${this.lastName[0].toUpperCase()}`;
} else {
return '';
}
}

@Expose()
firstName?: string;

@Expose()
lastName?: string;

@Expose()
email!: string;

@Expose()
@Type(() => RoleDto)
role!: RoleDto[];

@Expose()
@Type(() => GroupDto)
groups!: GroupDto[];

@Expose()
@IsDefined()
status!: SystemStatus;
}
5 changes: 4 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Module } from '@nestjs/common';
//import { MongooseModule } from '@nestjs/mongoose';
//import { MongooseModels } from './models';
import { CoreModule } from './core/core.module';

@Module({
imports: [],
imports: [CoreModule],
controllers: [],
providers: [],
})
Expand Down
18 changes: 18 additions & 0 deletions src/codes/codes.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CodesService } from './codes.service';

describe('CodesService', () => {
let service: CodesService;

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

service = module.get<CodesService>(CodesService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
14 changes: 14 additions & 0 deletions src/codes/codes.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Injectable } from '@nestjs/common';

@Injectable()
export class CodesService {
private codeToGroupId = {
'code123': 'group1',
'code456': 'group2',
}; // TODO zamienić na faktyczną baze danych

async validateCode(code: string): Promise<string | null> {
const groupId = this.codeToGroupId[code] || null;
return groupId;
}
}
11 changes: 11 additions & 0 deletions src/config/app-requirements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export class AppRequirements {
static readonly MAX_CODE_LENGTH = 20;
static readonly CODE_PATTERN = /^[a-z0-9-]+$/;

static validateCode(code: string): boolean {
if (code.length > this.MAX_CODE_LENGTH) {
return false;
}
return this.CODE_PATTERN.test(code);
}
}
24 changes: 24 additions & 0 deletions src/core/controllers/groups.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Controller, Post, Body, BadRequestException } from '@nestjs/common';
import { GroupsService } from '../services/groups.service';
import { JoinGroupDto } from 'src/core/dtos/join-group.dto';

@Controller('groups')
export class GroupsController {
constructor(private readonly groupsService: GroupsService) {}

@Post('join')
async joinGroup(
@Body() joinGroupDto: JoinGroupDto
): Promise<{ message: string }> {
const { userId, code } = joinGroupDto;

try {
return await this.groupsService.addUserToGroup(userId, code);
} catch (error) {
if (error instanceof BadRequestException) {
throw error;
}
throw new BadRequestException('Error during adding user to group');
}
}
}
11 changes: 11 additions & 0 deletions src/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { GroupsController } from './controllers/groups.controller';
import { GroupsService } from './services/groups.service';
import { CodesService } from './services/codes.service';

@Module({
imports: [],
controllers: [GroupsController],
providers: [GroupsService, CodesService],
})
export class CoreModule {}
14 changes: 14 additions & 0 deletions src/core/dtos/join-group.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IsNotEmpty, IsString, Matches, MaxLength } from 'class-validator';

export class JoinGroupDto {
@IsString()
@IsNotEmpty()
userId: string;

@IsString()
@IsNotEmpty()
@MaxLength(20)
@Matches(/^[aA-z0-9-]+$/)
code: string;
}
//dekorator match
18 changes: 18 additions & 0 deletions src/core/services/codes.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CodesService } from './codes.service';

describe('CodesService', () => {
let service: CodesService;

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

service = module.get<CodesService>(CodesService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
55 changes: 55 additions & 0 deletions src/core/services/codes.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Injectable } from '@nestjs/common';
import { JoinGroupDto } from 'src/core/dtos/join-group.dto';
import { validate } from 'class-validator';
@Injectable()
export class CodesService {
private codeToGroupId = {
sCode123: 'group1',
ucode456: 'group2',
acode789: 'group3',
}; // TODO zamienić na faktyczną bazę danych

async validateCode(code: string): Promise<{
groupId: string | null;
roleId: string | null;
status: string;
}> {
if (!this.isValidCode(code)) {
return { groupId: null, roleId: null, status: 'invalid' };
}

const roleId = this.extractRole(code);
const groupId = this.codeToGroupId[code] || null;
const status = this.getCodeStatus(code);
return { groupId, roleId, status };
}

private extractRole(code: string): string | null {
const roleMap = {
s: 'starosta',
u: 'student',
a: 'administrator',
};
const rolePrefix = code[0];
return roleMap[rolePrefix] || null;
}

private getCodeStatus(code: string): string {
// TODO: Replace this logic with actual status checking logic
const statusMap = {
sCode123: 'active',
ucode456: 'used',
acode789: 'expired',
};
return statusMap[code] || 'expired';
}

private async isValidCode(code: string): Promise<boolean> {
const validateCodeDto = new JoinGroupDto();
validateCodeDto.code = code;

const errors = await validate(validateCodeDto);
return errors.length === 0;
}
}
//musi zwracac id grupy i id roli jaka kod nadaje, a jak nie to falsz, moze byc uzyty, moze wygasc
18 changes: 18 additions & 0 deletions src/core/services/groups.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { GroupsService } from './groups.service';

describe('GroupsService', () => {
let service: GroupsService;

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

service = module.get<GroupsService>(GroupsService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
59 changes: 59 additions & 0 deletions src/core/services/groups.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
Injectable,
BadRequestException,
NotFoundException,
InternalServerErrorException,
} from '@nestjs/common';
import { CodesService } from './codes.service';

@Injectable()
export class GroupsService {
constructor(private readonly codesService: CodesService) {}

async groupExists(code: string): Promise<boolean> {
const validationResult = await this.codesService.validateCode(code);
return (
validationResult.groupId !== null && validationResult.status === 'active'
);
}

async addUserToGroup(
userId: string,
code: string
): Promise<{ message: string }> {
const validationResult = await this.codesService.validateCode(code);

if (!validationResult.groupId) {
throw new BadRequestException('Invalid code');
}

if (validationResult.status == 'expired') {
throw new BadRequestException('Inactive code');
}
if (validationResult.status == 'used') {
throw new BadRequestException('Used code');
}

try {
const groupId = validationResult.groupId;
const role = validationResult.roleId;

console.log(
`Dodaję użytkownika ${userId} do grupy ${groupId}, jako ${role}`
);
// TODO: Add logic

return { message: 'Student został pomyślnie dodany do grupy' };
} catch (error) {
if (error instanceof NotFoundException) {
throw new NotFoundException('Group not found');
}
throw new InternalServerErrorException('Error');
}
}

async removeStudentFromGroup(groupId: string, userId: string): Promise<void> {
console.log(`Usuwam użytkownika ${userId} z grupy ${groupId}`);
// TODO: Add logic
}
}
29 changes: 29 additions & 0 deletions src/groups/dtos/create-group.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Optional } from '@nestjs/common';
import { Expose } from 'class-transformer';
import { IsNotEmpty, IsString, MaxLength } from 'class-validator';
import { FieldConstraints } from 'src/libs/shared';

export class CreateGroupDto {
@Expose()
@IsString()
@IsNotEmpty()
@MaxLength(FieldConstraints.FIRST_NAME.MAX_LENGTH)
name!: string;

@Expose()
@Optional()
@IsString()
@MaxLength(FieldConstraints.COURSE_NAME.MAX_LENGTH)
courseName?: string;

@Expose()
@Optional()
@IsString()
@MaxLength(FieldConstraints.DESCRIPTION.MAX_LENGTH)
description?: string;

@Expose()
@IsString()
@IsNotEmpty()
avatar!: string;
}
Loading