Skip to content

Commit 054eaeb

Browse files
WIP Preferences
1 parent 37a02fe commit 054eaeb

File tree

8 files changed

+445
-1
lines changed

8 files changed

+445
-1
lines changed

service/src/core/core.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { CategoriesModule } from './categories/categories.module'
88
import { ProjectModule } from './projects/project.module'
99
import { TriggersModule } from './triggers/triggers.module';
1010
import { CrontabsModule } from './crontabs/crontabs.module';
11+
import { PreferencesModule } from './preferences/preferences.module';
1112

1213
@Module({
13-
imports: [AuthModule, UsersModule, CategoriesModule, ProjectModule, TriggersModule, CrontabsModule],
14+
imports: [AuthModule, UsersModule, CategoriesModule, ProjectModule, TriggersModule, CrontabsModule, PreferencesModule],
1415
providers: [CoreService],
1516
controllers: [CoreController],
1617
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { PartialType } from "@nestjs/swagger"
2+
3+
export class PreferencesCreateDto {
4+
5+
}
6+
7+
export class PreferencesUpdateDto extends PartialType(PreferencesCreateDto) {}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
2+
import { Document, ObjectId, Types } from 'mongoose'
3+
import { AbstractSchema } from '~/_common/abstracts/schemas/abstract.schema'
4+
5+
export type PreferencesDocument = Preferences & Document
6+
7+
@Schema({
8+
collection: 'preferences',
9+
versionKey: false,
10+
})
11+
export class Preferences extends AbstractSchema {
12+
@Prop({
13+
required: true,
14+
type: String,
15+
})
16+
public name: string
17+
18+
@Prop({
19+
required: true,
20+
type: Object,
21+
})
22+
public data: { [key: string]: any }
23+
24+
@Prop({
25+
required: true,
26+
type: Types.ObjectId,
27+
})
28+
public personId: ObjectId
29+
}
30+
31+
export const PreferencesSchema = SchemaFactory.createForClass(Preferences)
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import { DeleteResult } from 'mongodb'
2+
import { Test, TestingModule } from '@nestjs/testing'
3+
import { PreferencesController } from './preferences.controller'
4+
import { PreferencesService } from './preferences.service'
5+
import { PreferencesDto } from './_dto/preferences.dto'
6+
import { Preferences } from './_schemas/preferences.schema'
7+
import { HttpException, HttpStatus } from '@nestjs/common'
8+
import { Types } from 'mongoose'
9+
import { Response, Request } from 'express'
10+
import { getMockReq, getMockRes } from '@jest-mock/express'
11+
12+
describe('PreferencesController', () => {
13+
let controller: PreferencesController
14+
let service: PreferencesService
15+
const date = new Date()
16+
const _id = new Types.ObjectId()
17+
const { res, mockClear } = getMockRes()
18+
const object: Preferences = {
19+
_id,
20+
metadata: {
21+
createdAt: date,
22+
createdBy: 'console',
23+
lastUpdateAt: date,
24+
lastUpdateBy: 'console',
25+
},
26+
info: [],
27+
}
28+
29+
beforeEach(async () => {
30+
mockClear()
31+
const module: TestingModule = await Test.createTestingModule({
32+
controllers: [PreferencesController],
33+
providers: [
34+
PreferencesService,
35+
{
36+
provide: PreferencesService,
37+
useValue: {
38+
search: jest.fn().mockResolvedValue([[object], 1]),
39+
create: jest.fn().mockResolvedValue(object),
40+
read: jest.fn().mockResolvedValue(object),
41+
update: jest.fn().mockResolvedValue(object),
42+
remove: jest.fn().mockResolvedValue({
43+
acknowledged: true,
44+
deletedCount: 1,
45+
}),
46+
},
47+
},
48+
],
49+
}).compile()
50+
51+
controller = module.get<PreferencesController>(PreferencesController)
52+
service = module.get<PreferencesService>(PreferencesService)
53+
})
54+
55+
describe('search', () => {
56+
it('should return an array of Preferences objects and the total count', async () => {
57+
const req = getMockReq()
58+
const query = { _id: _id.toString() }
59+
const limit = 10
60+
const skip = 1
61+
const expectedResult: [Preferences[], number] = [[object], 1]
62+
jest.spyOn(service, 'search').mockImplementation(async () => await expectedResult)
63+
const response = await controller.search(req, res, query, limit.toString(), skip.toString(), null)
64+
expect(response.json).toHaveBeenCalledWith({ data: expectedResult[0], total: expectedResult[1] })
65+
expect(response.status).toHaveBeenCalledWith(HttpStatus.OK)
66+
})
67+
68+
it('should return an array of Preferences objects with default pagination', async () => {
69+
const req = getMockReq()
70+
const query = { _id: _id.toString() }
71+
const limit = 0
72+
const skip = 0
73+
const expectedResult: [Preferences[], number] = [[object], 1]
74+
jest.spyOn(service, 'search').mockImplementation(async () => await expectedResult)
75+
const response = await controller.search(req, res, query, limit.toString(), skip.toString(), null)
76+
expect(response.json).toHaveBeenCalledWith({ data: expectedResult[0], total: expectedResult[1] })
77+
expect(response.status).toHaveBeenCalledWith(HttpStatus.OK)
78+
})
79+
80+
it('should throw HttpException with BAD_REQUEST status when an error occurs', async () => {
81+
const req = {} as Request
82+
const res = {} as Response
83+
const query = { someParam: 'value' }
84+
const limit = 10
85+
const skip = 0
86+
jest.spyOn(service, 'search').mockRejectedValue(new Error('Something went wrong'))
87+
88+
try {
89+
await controller.search(req, res, query, limit.toString(), skip.toString(), null)
90+
} catch (error) {
91+
const status = error.getStatus()
92+
const response = error.getResponse()
93+
94+
expect(error).toBeInstanceOf(HttpException)
95+
expect(status).toBe(400)
96+
expect(response).toBe('Something went wrong')
97+
}
98+
})
99+
})
100+
101+
describe('read', () => {
102+
it('should return the data successfully', async () => {
103+
const expectedResult: Preferences = object
104+
jest.spyOn(service, 'read').mockImplementation(async () => await expectedResult)
105+
const result = await controller.read(_id.toString(), res)
106+
expect(res.status).toHaveBeenCalledWith(HttpStatus.OK)
107+
expect(res.json).toHaveBeenCalledWith(expectedResult)
108+
expect(result).toEqual(res)
109+
})
110+
111+
it('should throw an HttpException with the error message', async () => {
112+
const errorMessage = 'Error message'
113+
jest.spyOn(service, 'read').mockRejectedValue(new Error(errorMessage))
114+
try {
115+
await controller.read(_id.toString(), res)
116+
} catch (error) {
117+
expect(error.getStatus()).toBe(HttpStatus.BAD_REQUEST)
118+
expect(error.getResponse()).toBe(errorMessage)
119+
}
120+
})
121+
})
122+
123+
describe('create', () => {
124+
it('should return a Preferences object', async () => {
125+
const dto: PreferencesDto = { someProp: 'value' }
126+
const expectedResult = object
127+
jest.spyOn(service, 'create').mockImplementation(async () => await expectedResult)
128+
const response = await controller.create(dto, res)
129+
expect(response.status).toHaveBeenCalledWith(HttpStatus.CREATED)
130+
expect(response.json).toHaveBeenCalledWith(expectedResult)
131+
})
132+
133+
it('should throw HttpException with BAD_REQUEST status when an error occurs', async () => {
134+
const dto: PreferencesDto = { someProp: 'value' }
135+
jest.spyOn(service, 'create').mockRejectedValue(new Error('Something went wrong'))
136+
137+
try {
138+
await controller.create(dto, res)
139+
} catch (error) {
140+
expect(error).toBeInstanceOf(HttpException)
141+
expect(error.getStatus()).toBe(400)
142+
expect(error.getResponse()).toBe('Something went wrong')
143+
}
144+
})
145+
})
146+
147+
describe('update', () => {
148+
it('should return a Preferences object', async () => {
149+
const id = _id.toString()
150+
const dto: PreferencesDto = { someProp: 'value' }
151+
const expectedResult: Preferences = object
152+
jest.spyOn(service, 'update').mockImplementation(async () => await expectedResult)
153+
const response = await controller.update(id, dto, res)
154+
expect(response.status).toHaveBeenCalledWith(HttpStatus.OK)
155+
expect(response.json).toHaveBeenCalledWith(expectedResult)
156+
})
157+
158+
it('should throw HttpException with BAD_REQUEST status when an error occurs', async () => {
159+
const id = _id.toString()
160+
const dto: PreferencesDto = { someProp: 'value' }
161+
jest.spyOn(service, 'update').mockRejectedValue(new Error('Something went wrong'))
162+
163+
try {
164+
await controller.update(id, dto, res)
165+
} catch (error) {
166+
expect(error).toBeInstanceOf(HttpException)
167+
expect(error.getStatus()).toBe(400)
168+
expect(error.getResponse()).toBe('Something went wrong')
169+
}
170+
})
171+
})
172+
173+
describe('remove', () => {
174+
it('should return a Preferences object', async () => {
175+
const id = _id.toString()
176+
const expectedResult: DeleteResult = {
177+
acknowledged: true,
178+
deletedCount: 1,
179+
}
180+
jest.spyOn(service, 'remove').mockImplementation(async () => await expectedResult)
181+
const response = await controller.remove(id, res)
182+
expect(response.status).toHaveBeenCalledWith(HttpStatus.OK)
183+
expect(response.json).toHaveBeenCalledWith(expectedResult)
184+
})
185+
186+
it('should throw HttpException with BAD_REQUEST status when an error occurs', async () => {
187+
const id = _id.toString()
188+
jest.spyOn(service, 'remove').mockRejectedValue(new Error('Something went wrong'))
189+
190+
try {
191+
await controller.remove(id, res)
192+
} catch (error) {
193+
expect(error).toBeInstanceOf(HttpException)
194+
expect(error.getStatus()).toBe(400)
195+
expect(error.getResponse()).toBe('Something went wrong')
196+
}
197+
})
198+
})
199+
})
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Body, Controller, Delete, Get, HttpStatus, Param, Patch, Post, Req, Res } from '@nestjs/common'
2+
import { PreferencesCreateDto, PreferencesUpdateDto } from './_dto/preferences.dto'
3+
import { PreferencesService } from './preferences.service'
4+
import { AbstractController } from '~/_common/abstracts/abstract.controller'
5+
import { ApiParam } from '@nestjs/swagger'
6+
import { SearchFilterSchema, FilterSchema, SearchFilterOptions, FilterOptions, ObjectIdValidationPipe } from '@streamkits/nestjs_module_scrud'
7+
import { Types } from 'mongoose'
8+
import { Request, Response } from 'express'
9+
10+
@Controller('preferences')
11+
export class PreferencesController extends AbstractController {
12+
public readonly projection = {
13+
name: 1,
14+
data: 1,
15+
personId: 1,
16+
}
17+
18+
constructor(private readonly _service: PreferencesService) {
19+
super()
20+
}
21+
22+
@Post()
23+
public async create(@Req() req: Request, @Res() res: Response, @Body() body: PreferencesCreateDto) {
24+
const data = await this._service.create(body)
25+
return res.status(HttpStatus.CREATED).json({
26+
statusCode: HttpStatus.CREATED,
27+
data,
28+
})
29+
}
30+
31+
@Get()
32+
public async search(@Res() res: Response, @SearchFilterSchema() searchFilterSchema: FilterSchema, @SearchFilterOptions() searchFilterOptions: FilterOptions) {
33+
const [data, total] = await this._service.findAndCount(searchFilterSchema, this.projection, searchFilterOptions)
34+
return res.status(HttpStatus.OK).json({
35+
statusCode: HttpStatus.OK,
36+
total,
37+
data,
38+
})
39+
}
40+
41+
@Get(':_id([0-9a-fA-F]{24})')
42+
@ApiParam({ name: '_id', type: String })
43+
public async read(@Param('_id', ObjectIdValidationPipe) _id: Types.ObjectId, @Res() res: Response) {
44+
const data = await this._service.findById(_id)
45+
return res.status(HttpStatus.OK).json({
46+
statusCode: HttpStatus.OK,
47+
data,
48+
})
49+
}
50+
51+
@Patch(':_id([0-9a-fA-F]{24})')
52+
@ApiParam({ name: '_id', type: String })
53+
public async update(@Param('_id', ObjectIdValidationPipe) _id: Types.ObjectId, @Body() body: PreferencesUpdateDto, @Res() res: Response) {
54+
const data = await this._service.update(_id, body)
55+
return res.status(HttpStatus.OK).json({
56+
statusCode: HttpStatus.OK,
57+
data,
58+
})
59+
}
60+
61+
@Delete(':_id([0-9a-fA-F]{24})')
62+
@ApiParam({ name: '_id', type: String })
63+
public async remove(@Param('_id', ObjectIdValidationPipe) _id: Types.ObjectId, @Res() res: Response) {
64+
const data = await this._service.delete(_id)
65+
return res.status(HttpStatus.OK).json({
66+
statusCode: HttpStatus.OK,
67+
data,
68+
})
69+
}
70+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Module } from '@nestjs/common'
2+
import { MongooseModule } from '@nestjs/mongoose'
3+
import { PreferencesSchema, Preferences } from './_schemas/preferences.schema'
4+
import { PreferencesService } from './preferences.service'
5+
import { PreferencesController } from './preferences.controller'
6+
7+
@Module({
8+
imports: [
9+
MongooseModule.forFeatureAsync([
10+
{
11+
name: Preferences.name,
12+
useFactory: () => PreferencesSchema,
13+
},
14+
]),
15+
],
16+
providers: [PreferencesService],
17+
controllers: [PreferencesController],
18+
})
19+
export class PreferencesModule {}

0 commit comments

Comments
 (0)