Skip to content
Draft
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
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { MembersModule } from './members/members.module';
import { GithubGqlModule } from './github-gql/github-gql.module';
import { ProjectsModule } from './projects/projects.module';
import { LeaderboardModule } from './leaderboard/leaderboard.module';
import { CoreRecordsModule } from './core-records/core-records.module';

@Module({
imports: [
Expand Down Expand Up @@ -47,6 +48,7 @@ import { LeaderboardModule } from './leaderboard/leaderboard.module';
GithubGqlModule,
ProjectsModule,
LeaderboardModule,
CoreRecordsModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
29 changes: 29 additions & 0 deletions src/common/mongoose/schemas/core-records.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CoreRecordTypeName, ICoreRecord } from '@/types/core-records';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { type HydratedDocument } from 'mongoose';

export type CoreRecordDocument = HydratedDocument<CoreRecord>;

@Schema()
export class CoreRecord implements ICoreRecord {
@Prop({ required: true, refPath: 'type' })
recordId: string;

@Prop({ required: true, enum: CoreRecordTypeName })
type: CoreRecordTypeName;

@Prop({ required: true })
createdAt: Date;

@Prop({ required: true })
createdBy: string;

@Prop({ required: false })
archivedAt: Date;

@Prop({ required: false })
archivedBy: string;
}

export type FilterCoreRecords = Partial<Record<keyof CoreRecord, string>>;
export const CoreRecordSchema = SchemaFactory.createForClass(CoreRecord);
31 changes: 31 additions & 0 deletions src/common/mongoose/schemas/records/memberRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { IMemberRecord } from '@/types/core-records';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { type HydratedDocument } from 'mongoose';

export type MemberRecordDocument = HydratedDocument<MemberRecord>;

@Schema()
export class MemberRecord implements IMemberRecord {
@Prop({ required: true })
name: string;

@Prop({ required: true })
discordUser: string;

@Prop({
required: true,
type: {
github: String,
linkedIn: String,
},
})
links: {
github: string;
linkedIn: string;
};

@Prop({ required: true })
description: string;
}

export const MemberRecordSchema = SchemaFactory.createForClass(MemberRecord);
13 changes: 13 additions & 0 deletions src/common/mongoose/schemas/records/mentorRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IMentorRecord } from '@/types/core-records';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { type HydratedDocument } from 'mongoose';

export type MentorRecordDocument = HydratedDocument<MentorRecord>;

@Schema()
export class MentorRecord implements IMentorRecord {
@Prop({ required: true })
stud: string;
}

export const MentorRecordSchema = SchemaFactory.createForClass(MentorRecord);
16 changes: 16 additions & 0 deletions src/common/mongoose/schemas/records/projectRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IProjectRecord } from '@/types/core-records';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { type HydratedDocument } from 'mongoose';

export type ProjectRecordDocument = HydratedDocument<ProjectRecord>;

@Schema()
export class ProjectRecord implements IProjectRecord {
@Prop({ required: true })
githubLink: string;

@Prop({ required: true })
discordLink: string;
}

export const ProjectRecordSchema = SchemaFactory.createForClass(ProjectRecord);
11 changes: 11 additions & 0 deletions src/common/mongoose/schemas/type/RecordType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { MemberRecordSchema } from '@/common/mongoose/schemas/records/memberRecord';
import { ProjectRecordSchema } from '../records/projectRecord';
import { MentorRecordSchema } from '../records/mentorRecord';

export const AllowedRecordDocumentSchemas = [
MemberRecordSchema,
ProjectRecordSchema,
MentorRecordSchema,
] as const;

export type RecordDocumentType = (typeof AllowedRecordDocumentSchemas)[number];
47 changes: 47 additions & 0 deletions src/core-records/core-records.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CoreRecordsController } from './core-records.controller';
import { CoreRecordsService } from './core-records.service';

describe('CoreRecordsController', () => {
let controller: CoreRecordsController;
let mockCoreRecordsService: Partial<CoreRecordsService>;

beforeEach(async () => {
mockCoreRecordsService = {
getAllRecords: jest.fn(),
};

const module: TestingModule = await Test.createTestingModule({
controllers: [CoreRecordsController],
providers: [
{
provide: CoreRecordsService,
useValue: mockCoreRecordsService,
},
],
}).compile();

controller = module.get<CoreRecordsController>(CoreRecordsController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});

it('getAllRecords should call service with correct parameters', async () => {
const filter = {
type: 'type',
};
await controller.getAllRecords({
page: 1,
limit: 10,
filter,
});

expect(mockCoreRecordsService.getAllRecords).toHaveBeenCalledWith({
page: 1,
limit: 10,
filter,
});
});
});
41 changes: 41 additions & 0 deletions src/core-records/core-records.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Controller } from '@nestjs/common';
import { CoreRecordsService } from './core-records.service';
import { FilterCoreRecords } from '@/common/mongoose/schemas/core-records';
import { CoreRecordTypeName } from '@/types/core-records';

@Controller('core-records')
export class CoreRecordsController {
constructor(private readonly coreRecordsService: CoreRecordsService) {}

// Add controller methods here
// get all records paginated and filtered
async getAllRecords({
page,
limit,
filter,
}: {
page: number;
limit: number;
filter: FilterCoreRecords;
}) {
return this.coreRecordsService.getAllRecords({ page, limit, filter });
}

// get a single record by id
async getRecordById(id: string) {
return this.coreRecordsService.getRecordById(id);
}

// create a new record
async createRecord(
recordId: string,
recordType: CoreRecordTypeName,
createdBy: string,
) {
return this.coreRecordsService.createRecord(
recordId,
recordType,
createdBy,
);
}
}
19 changes: 19 additions & 0 deletions src/core-records/core-records.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';
import { CoreRecordsService } from './core-records.service';
import { CoreRecordsController } from './core-records.controller';
import { MongooseModule } from '@nestjs/mongoose';
import {
CoreRecord,
CoreRecordSchema,
} from '@/common/mongoose/schemas/core-records';

@Module({
providers: [CoreRecordsService],
controllers: [CoreRecordsController],
imports: [
MongooseModule.forFeature([
{ name: CoreRecord.name, schema: CoreRecordSchema },
]),
],
})
export class CoreRecordsModule {}
35 changes: 35 additions & 0 deletions src/core-records/core-records.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CoreRecordsService } from './core-records.service';
import {
TestDbModule,
closeInMongodConnection,
} from '../../test/mocks/module/mongo-in-memory';
import { MongooseModule } from '@nestjs/mongoose';
import { CoreRecordSchema } from '@/common/mongoose/schemas/core-records';

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

beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [CoreRecordsService],
imports: [
TestDbModule,
MongooseModule.forFeature([
{ name: 'CoreRecord', schema: CoreRecordSchema },
]),
// Include any setup for in-memory MongoDB here
],
}).compile();

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

afterAll(async () => {
await closeInMongodConnection(); // Close the database connection after all tests
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
130 changes: 130 additions & 0 deletions src/core-records/core-records.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import {
CoreRecord,
CoreRecordDocument,
FilterCoreRecords,
} from '@/common/mongoose/schemas/core-records';
import { CoreRecordTypeName } from '@/types/core-records';
import { Injectable, Logger } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';

@Injectable()
export class CoreRecordsService {
constructor(
@InjectModel(CoreRecord.name)
private readonly coreRecordModel: Model<CoreRecordDocument>,
) {}
// Get all records paginated and filtered
async getAllRecords({
page,
limit,
filter,
}: {
page: number;
limit: number;
filter: FilterCoreRecords;
}) {
try {
filter.archivedAt = undefined;
const query = this.coreRecordModel.find(filter);

const total = await this.coreRecordModel.countDocuments(filter).exec();
const data = await query
.skip((page - 1) * limit)
.limit(limit)
.exec();

return { data, total };
} catch (error) {
Logger.error('Error getting all records', { filter, error });
return null;
}
}

// get a single record by id
async getRecordById(id: string) {
try {
const record = await this.coreRecordModel
.find({
_id: id,
archivedAt: null,
})
.exec();
return record;
} catch (error) {
Logger.error('Error getting record by id', { id, error });
return null;
}
}

async createRecord(
recordId: string,
recordType: CoreRecordTypeName,
createdBy: string,
) {
try {
const newRecord = new this.coreRecordModel({
recordId,
type: recordType,
createdAt: new Date(),
createdBy,
updatedAt: new Date(),
updatedBy: createdBy,
});
await newRecord.save();
return newRecord;
} catch (error) {
Logger.error('Error saving record', {
recordId,
createdBy,
recordType,
error,
});
}
}

//update a record by id
async updateRecord(
id: string,
recordId: string,
updatedBy: string,
): Promise<CoreRecordDocument | null> {
try {
const updatedRecord = await this.coreRecordModel
.findByIdAndUpdate(
id,
{
recordId,
updatedAt: new Date(),
updatedBy,
},
{ new: true },
)
.exec();
return updatedRecord;
} catch (error) {
Logger.error('Error updating record', { id, recordId, updatedBy, error });
return null;
}
}

// delete a record by id
async deleteRecord(id: string, deletedBy: string) {
try {
const deletedRecord = await this.coreRecordModel
.findByIdAndUpdate(
id,
{
archivedAt: new Date(),
archivedBy: deletedBy,
},
{ new: true },
)
.exec();
return deletedRecord;
} catch (error) {
Logger.error('Error deleting record', { id, deletedBy, error });
return null;
}
}
}
Loading