From 57f1190625ebedd99685749d307dc54a54b3ee69 Mon Sep 17 00:00:00 2001 From: gganebnyi Date: Sat, 18 Feb 2023 23:38:43 +0200 Subject: [PATCH 1/4] added automatic updates for createdAt and updateAt fields --- .../common/schema/auditable-entity.schema.ts | 18 ++++++++++++++++++ nestjs-mongoose/src/posts/post.schema.ts | 10 ++++------ nestjs-mongoose/src/posts/posts.service.ts | 8 +++----- 3 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 nestjs-mongoose/src/common/schema/auditable-entity.schema.ts diff --git a/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts b/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts new file mode 100644 index 0000000..8ed2580 --- /dev/null +++ b/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts @@ -0,0 +1,18 @@ +import { Prop } from "@nestjs/mongoose"; +import { now } from "mongoose"; + +export class AuditableEntity { + + @Prop({ default: now() }) + createdAt: Date; + + @Prop() + createdBy: String; + + @Prop({ default: now() }) + updatedAt: Date; + + @Prop() + updatedBy: String; + +} \ No newline at end of file diff --git a/nestjs-mongoose/src/posts/post.schema.ts b/nestjs-mongoose/src/posts/post.schema.ts index cfad338..88f0c99 100644 --- a/nestjs-mongoose/src/posts/post.schema.ts +++ b/nestjs-mongoose/src/posts/post.schema.ts @@ -1,18 +1,16 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; -import { now, Document } from 'mongoose'; +import { Document } from 'mongoose'; +import { AuditableEntity } from 'src/common/schema/auditable-entity.schema'; export type PostDocument = Post & Document; -@Schema() -export class Post { +@Schema({ timestamps: true }) +export class Post extends AuditableEntity { @Prop() title: string; @Prop() content: string; - - @Prop({ default: now() }) - createdAt: Date; } export const PostSchema = SchemaFactory.createForClass(Post); diff --git a/nestjs-mongoose/src/posts/posts.service.ts b/nestjs-mongoose/src/posts/posts.service.ts index e02eeab..a9bf81e 100644 --- a/nestjs-mongoose/src/posts/posts.service.ts +++ b/nestjs-mongoose/src/posts/posts.service.ts @@ -15,7 +15,7 @@ class PostsService { } async findOne(id: string) { - const post = await this.postModel.findOne({ id }); + const post = await this.postModel.findById(id); if (!post) { throw new NotFoundException(); } @@ -39,10 +39,8 @@ class PostsService { async partialUpdate(id: string, postData: CreatePostsDto) { const post = await this.postModel - .findByIdAndUpdate(id, { - $set: postData, - }) - .setOptions({ overwrite: true, new: true }); + .findByIdAndUpdate(id, postData) + .setOptions({ new: true }); if (!post) { throw new NotFoundException(); } From 5eeec26d87ac18fbc4e13e3af95ef30382ed5cf6 Mon Sep 17 00:00:00 2001 From: gganebnyi Date: Sun, 19 Feb 2023 19:48:38 +0200 Subject: [PATCH 2/4] tests implementation --- nestjs-mongoose/package.json | 3 +- nestjs-mongoose/src/app.controller.spec.ts | 2 +- nestjs-mongoose/src/app.controller.ts | 9 +- nestjs-mongoose/src/app.service.ts | 6 +- .../common/schema/auditable-entity.schema.ts | 8 +- nestjs-mongoose/src/posts/post.schema.ts | 2 +- .../src/posts/posts.service.spec.ts | 93 +++++++++++ .../mongo/root-mongoose-test.module.ts | 19 +++ nestjs-mongoose/yarn.lock | 157 +++++++++++++++++- 9 files changed, 282 insertions(+), 17 deletions(-) create mode 100644 nestjs-mongoose/src/posts/posts.service.spec.ts create mode 100644 nestjs-mongoose/src/test-utils/mongo/root-mongoose-test.module.ts diff --git a/nestjs-mongoose/package.json b/nestjs-mongoose/package.json index 12de337..30a8b13 100644 --- a/nestjs-mongoose/package.json +++ b/nestjs-mongoose/package.json @@ -32,6 +32,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.13.2", "joi": "^17.7.0", + "mongodb-memory-server": "^8.11.4", "mongoose": "^6.7.2", "nats": "^2.6.1", "reflect-metadata": "^0.1.13", @@ -79,4 +80,4 @@ "coverageDirectory": "../coverage", "testEnvironment": "node" } -} \ No newline at end of file +} diff --git a/nestjs-mongoose/src/app.controller.spec.ts b/nestjs-mongoose/src/app.controller.spec.ts index 9209695..d22f389 100644 --- a/nestjs-mongoose/src/app.controller.spec.ts +++ b/nestjs-mongoose/src/app.controller.spec.ts @@ -16,7 +16,7 @@ describe('AppController', () => { describe('root', () => { it('should return "Hello World!"', () => { - return 'Hello World!'; + expect(appController.getHello()).toBe('Hello World!'); }); }); }); diff --git a/nestjs-mongoose/src/app.controller.ts b/nestjs-mongoose/src/app.controller.ts index c73d6d8..61c9c36 100644 --- a/nestjs-mongoose/src/app.controller.ts +++ b/nestjs-mongoose/src/app.controller.ts @@ -1,7 +1,12 @@ -import { Controller } from '@nestjs/common'; +import { Controller, Get } from '@nestjs/common'; import { AppService } from './app.service'; @Controller() export class AppController { - constructor(private readonly appService: AppService) {} + constructor(private readonly appService: AppService) { } + + @Get() + getHello(): string { + return this.appService.getHello(); + } } diff --git a/nestjs-mongoose/src/app.service.ts b/nestjs-mongoose/src/app.service.ts index 7263d33..927d7cc 100644 --- a/nestjs-mongoose/src/app.service.ts +++ b/nestjs-mongoose/src/app.service.ts @@ -1,4 +1,8 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class AppService {} +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts b/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts index 8ed2580..e4ffebf 100644 --- a/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts +++ b/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts @@ -4,15 +4,15 @@ import { now } from "mongoose"; export class AuditableEntity { @Prop({ default: now() }) - createdAt: Date; + createdAt?: Date; @Prop() - createdBy: String; + createdBy?: String; @Prop({ default: now() }) - updatedAt: Date; + updatedAt?: Date; @Prop() - updatedBy: String; + updatedBy?: String; } \ No newline at end of file diff --git a/nestjs-mongoose/src/posts/post.schema.ts b/nestjs-mongoose/src/posts/post.schema.ts index 88f0c99..4230936 100644 --- a/nestjs-mongoose/src/posts/post.schema.ts +++ b/nestjs-mongoose/src/posts/post.schema.ts @@ -1,6 +1,6 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; -import { AuditableEntity } from 'src/common/schema/auditable-entity.schema'; +import { AuditableEntity } from '../common/schema/auditable-entity.schema'; export type PostDocument = Post & Document; diff --git a/nestjs-mongoose/src/posts/posts.service.spec.ts b/nestjs-mongoose/src/posts/posts.service.spec.ts new file mode 100644 index 0000000..9356d3f --- /dev/null +++ b/nestjs-mongoose/src/posts/posts.service.spec.ts @@ -0,0 +1,93 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { getModelToken, MongooseModule } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { NotFoundException } from '@nestjs/common'; +import CreatePostsDto from './dto/create-posts.dto'; +import { Post, PostDocument, postForFeature } from './post.schema'; +import PostsService from './posts.service'; +import { PaginationParamsDto } from '../common/pagination/pagination.dto'; +import * as mongoose from 'mongoose'; +import { MongoMemoryServer } from 'mongodb-memory-server'; +import { DataWithPaginationDto } from '../common/pagination/pagination.dto'; +import { rootMongooseTestModule } from '../test-utils/mongo/root-mongoose-test.module'; + +describe('PostsService', () => { + let service: PostsService; + let model: Model; + let mongoServer: MongoMemoryServer; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = await mongoServer.getUri(); + await mongoose.connect(mongoUri); + }); + + afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + rootMongooseTestModule(), + MongooseModule.forFeature([postForFeature]), + ], + providers: [PostsService], + }).compile(); + + service = module.get(PostsService); + model = module.get>(getModelToken(Post.name)); + }); + + describe('findAll', () => { + it('should return paginated posts', async () => { + const posts: Partial[] = []; + for (let i = 0; i < 20; i++) { + const postData: CreatePostsDto = { title: `Post ${i}`, content: `Content ${i}` }; + const post = await model.create(postData); + posts.push({ id: post._id, ...postData }); + } + + const paginationParams: PaginationParamsDto = { + limit: 10, + offset: 0, + }; + + const expectedData = posts.slice(0, 10); + const expectedMeta = { + total: 20, + limit: paginationParams.limit, + offset: paginationParams.offset, + }; + const expectedResult: DataWithPaginationDto> = { + data: expectedData, + meta: expectedMeta, + }; + + const result = await service.findAll(paginationParams); + expect(result).toEqual(expectedResult); + }); + }); + + describe('findOne', () => { + it('should return the specified post', async () => { + const post = await model.create({ title: 'Post Title', content: 'Post Content' }); + const result = await service.findOne(post._id); + expect(result).toEqual(post); + }); + + it('should throw a NotFoundException if the post is not found', async () => { + await expect(service.findOne('invalid-id')).rejects.toThrow(NotFoundException); + }); + }); + + describe('create', () => { + it('should create and return the new post', async () => { + const postData: CreatePostsDto = { title: 'New Post Title', content: 'New Post Content' }; + const result = await service.create(postData); + expect(result).toMatchObject(postData); + }); + }); + +}) \ No newline at end of file diff --git a/nestjs-mongoose/src/test-utils/mongo/root-mongoose-test.module.ts b/nestjs-mongoose/src/test-utils/mongo/root-mongoose-test.module.ts new file mode 100644 index 0000000..0ce5f4d --- /dev/null +++ b/nestjs-mongoose/src/test-utils/mongo/root-mongoose-test.module.ts @@ -0,0 +1,19 @@ +import { MongooseModule, MongooseModuleOptions } from '@nestjs/mongoose'; +import { MongoMemoryServer } from 'mongodb-memory-server'; + +let mongod: MongoMemoryServer; + +export const rootMongooseTestModule = (options: MongooseModuleOptions = {}) => MongooseModule.forRootAsync({ + useFactory: async () => { + mongod = await MongoMemoryServer.create(); + const mongoUri = await mongod.getUri(); + return { + uri: mongoUri, + ...options, + } + }, +}); + +export const closeInMongodConnection = async () => { + if (mongod) await mongod.stop(); +} \ No newline at end of file diff --git a/nestjs-mongoose/yarn.lock b/nestjs-mongoose/yarn.lock index 93e0873..7694c64 100644 --- a/nestjs-mongoose/yarn.lock +++ b/nestjs-mongoose/yarn.lock @@ -1797,6 +1797,11 @@ dependencies: "@types/superagent" "*" +"@types/tmp@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.3.tgz#908bfb113419fd6a42273674c00994d40902c165" + integrity sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA== + "@types/webidl-conversions@*": version "7.0.0" resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz#2b8e60e33906459219aa587e9d1a612ae994cfe7" @@ -2217,6 +2222,13 @@ asap@^2.0.0: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +async-mutex@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" + integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA== + dependencies: + tslib "^2.3.1" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2306,7 +2318,7 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^4.1.0: +bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== @@ -2389,6 +2401,11 @@ bson@^4.7.0: dependencies: buffer "^5.6.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -2432,7 +2449,7 @@ camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: +camelcase@^6.2.0, camelcase@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -2611,6 +2628,11 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + component-emitter@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -2872,7 +2894,7 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -end-of-stream@^1.1.0: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -3227,6 +3249,13 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -3261,6 +3290,15 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" +find-cache-dir@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -3350,6 +3388,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@10.1.0, fs-extra@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" @@ -3403,6 +3446,11 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + get-stream@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -3539,7 +3587,7 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" -https-proxy-agent@^5.0.0: +https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -4422,7 +4470,7 @@ magic-string@0.25.7: dependencies: sourcemap-codec "^1.4.4" -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -4441,6 +4489,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +md5-file@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-5.0.0.tgz#e519f631feca9c39e7f9ea1780b63c4745012e20" + integrity sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4545,6 +4598,36 @@ mongodb-connection-string-url@^2.5.4: "@types/whatwg-url" "^8.2.1" whatwg-url "^11.0.0" +mongodb-memory-server-core@8.11.4: + version "8.11.4" + resolved "https://registry.yarnpkg.com/mongodb-memory-server-core/-/mongodb-memory-server-core-8.11.4.tgz#8296f9207c47d2464adc629c22b2bdcf54865ff6" + integrity sha512-VWUstcegjwaGTZzaaSLmKuEtxhbNPRM87lq1oFHzYC07pzyxAfjIELcYRKef2O+XlhKlVQ9x2VqpuvTF97h7SA== + dependencies: + "@types/tmp" "^0.2.3" + async-mutex "^0.3.2" + camelcase "^6.3.0" + debug "^4.3.4" + find-cache-dir "^3.3.2" + get-port "^5.1.1" + https-proxy-agent "^5.0.1" + md5-file "^5.0.0" + mongodb "^4.13.0" + new-find-package-json "^2.0.0" + semver "^7.3.8" + tar-stream "^2.1.4" + tmp "^0.2.1" + tslib "^2.4.1" + uuid "^9.0.0" + yauzl "^2.10.0" + +mongodb-memory-server@^8.11.4: + version "8.11.4" + resolved "https://registry.yarnpkg.com/mongodb-memory-server/-/mongodb-memory-server-8.11.4.tgz#5f4bfb31e39f4c56947d345e6af566ee021e6af3" + integrity sha512-DI6iXKsZBpMFAPxj/+35jl2s1LxVTpBc+VUpMzEe9Mvm73XIzIzPdU5Aa3asO7RfRSeR5l5wn0qpVAkvP1BScg== + dependencies: + mongodb-memory-server-core "8.11.4" + tslib "^2.4.1" + mongodb@4.11.0: version "4.11.0" resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.11.0.tgz#d28fdc7509f24d0d274f456529441fa3e570415c" @@ -4558,6 +4641,18 @@ mongodb@4.11.0: "@aws-sdk/credential-providers" "^3.186.0" saslprep "^1.0.3" +mongodb@^4.13.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.14.0.tgz#3a21ca602069b2494b7066a7709056c8cece6bf6" + integrity sha512-coGKkWXIBczZPr284tYKFLg+KbGPPLlSbdgfKAb6QqCFt5bo5VFZ50O3FFzsw4rnkqjwT6D8Qcoo9nshYKM7Mg== + dependencies: + bson "^4.7.0" + mongodb-connection-string-url "^2.5.4" + socks "^2.7.1" + optionalDependencies: + "@aws-sdk/credential-providers" "^3.186.0" + saslprep "^1.0.3" + mongoose@^6.7.2: version "6.7.3" resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-6.7.3.tgz#1ec0d8cabcb53ecc171d17baaf4a68356ab470ed" @@ -4644,6 +4739,13 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +new-find-package-json@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/new-find-package-json/-/new-find-package-json-2.0.0.tgz#96553638781db35061f351e8ccb4d07126b6407d" + integrity sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew== + dependencies: + debug "^4.3.4" + nkeys.js@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/nkeys.js/-/nkeys.js-1.0.3.tgz#487688a6f4f36f4a2796eee000cc6e54e122cb08" @@ -4875,6 +4977,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -4890,7 +4997,7 @@ pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== -pkg-dir@^4.2.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== @@ -5041,7 +5148,7 @@ readable-stream@^2.2.2: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.4.0: +readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -5525,6 +5632,17 @@ tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -5585,6 +5703,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -5718,6 +5843,11 @@ tslib@^2.1.0, tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tslib@^2.4.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -5842,6 +5972,11 @@ uuid@8.3.2, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -6100,6 +6235,14 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From a1462d904b28b9775c765ff5df78e9c3d385a67d Mon Sep 17 00:00:00 2001 From: gganebnyi Date: Mon, 20 Feb 2023 23:51:12 +0200 Subject: [PATCH 3/4] created unit test for PostsService --- ...create-posts.dto.ts => create-post.dto.ts} | 4 +- .../src/posts/dto/get-posts.dto.ts | 6 ++ .../src/posts/dto/patch-post.dto.ts | 13 +++ .../posts/dto/{posts.dto.ts => post.dto.ts} | 6 +- .../src/posts/dto/update-post.dto.ts | 12 +-- .../src/posts/dto/update-posts.dto.ts | 16 ---- nestjs-mongoose/src/posts/posts.controller.ts | 27 +++--- .../src/posts/posts.service.spec.ts | 93 +++++++++++++++++-- nestjs-mongoose/src/posts/posts.service.ts | 18 ++-- 9 files changed, 131 insertions(+), 64 deletions(-) rename nestjs-mongoose/src/posts/dto/{create-posts.dto.ts => create-post.dto.ts} (72%) create mode 100644 nestjs-mongoose/src/posts/dto/get-posts.dto.ts create mode 100644 nestjs-mongoose/src/posts/dto/patch-post.dto.ts rename nestjs-mongoose/src/posts/dto/{posts.dto.ts => post.dto.ts} (73%) delete mode 100644 nestjs-mongoose/src/posts/dto/update-posts.dto.ts diff --git a/nestjs-mongoose/src/posts/dto/create-posts.dto.ts b/nestjs-mongoose/src/posts/dto/create-post.dto.ts similarity index 72% rename from nestjs-mongoose/src/posts/dto/create-posts.dto.ts rename to nestjs-mongoose/src/posts/dto/create-post.dto.ts index dc3183d..7da000e 100644 --- a/nestjs-mongoose/src/posts/dto/create-posts.dto.ts +++ b/nestjs-mongoose/src/posts/dto/create-post.dto.ts @@ -1,6 +1,6 @@ import { IsString, IsNotEmpty } from 'class-validator'; -export class CreatePostsDto { +export class CreatePostDto { @IsString() @IsNotEmpty() title: string; @@ -10,4 +10,4 @@ export class CreatePostsDto { content: string; } -export default CreatePostsDto; +export default CreatePostDto; diff --git a/nestjs-mongoose/src/posts/dto/get-posts.dto.ts b/nestjs-mongoose/src/posts/dto/get-posts.dto.ts new file mode 100644 index 0000000..476b0ff --- /dev/null +++ b/nestjs-mongoose/src/posts/dto/get-posts.dto.ts @@ -0,0 +1,6 @@ +import { DataWithPaginationDto, PaginationParamsDto } from "../../common/pagination/pagination.dto"; +import { PostDto } from "./post.dto"; + +export class GetPostsDto extends PaginationParamsDto { } + +export class GetPostsResponseDto extends DataWithPaginationDto { } diff --git a/nestjs-mongoose/src/posts/dto/patch-post.dto.ts b/nestjs-mongoose/src/posts/dto/patch-post.dto.ts new file mode 100644 index 0000000..4eea587 --- /dev/null +++ b/nestjs-mongoose/src/posts/dto/patch-post.dto.ts @@ -0,0 +1,13 @@ +import { IsOptional, IsString } from 'class-validator'; + +export class PatchPostDto { + @IsString() + @IsOptional() + title?: string; + + @IsString() + @IsOptional() + content?: string; +} + +export default PatchPostDto; diff --git a/nestjs-mongoose/src/posts/dto/posts.dto.ts b/nestjs-mongoose/src/posts/dto/post.dto.ts similarity index 73% rename from nestjs-mongoose/src/posts/dto/posts.dto.ts rename to nestjs-mongoose/src/posts/dto/post.dto.ts index dafd7c7..20b8406 100644 --- a/nestjs-mongoose/src/posts/dto/posts.dto.ts +++ b/nestjs-mongoose/src/posts/dto/post.dto.ts @@ -1,7 +1,7 @@ import { Expose, Transform } from '@nestjs/class-transformer'; import { DataWithPaginationDto, PaginationParamsDto } from '../../common/pagination/pagination.dto'; -export class PostsDto { +export class PostDto { @Expose() id: string; @@ -18,7 +18,3 @@ export class PostsDto { ) createdAt: Date | null; } - -export class GetPostsDto extends PaginationParamsDto { } - -export class GetPostsResponseDto extends DataWithPaginationDto { } diff --git a/nestjs-mongoose/src/posts/dto/update-post.dto.ts b/nestjs-mongoose/src/posts/dto/update-post.dto.ts index 55d9b88..6b0753a 100644 --- a/nestjs-mongoose/src/posts/dto/update-post.dto.ts +++ b/nestjs-mongoose/src/posts/dto/update-post.dto.ts @@ -1,13 +1,5 @@ -import { IsOptional, IsString } from 'class-validator'; +import CreatePostDto from './create-post.dto'; -export class UpdatePostDto { - @IsString() - @IsOptional() - title: string; - - @IsString() - @IsOptional() - content: string; -} +export class UpdatePostDto extends CreatePostDto { } export default UpdatePostDto; diff --git a/nestjs-mongoose/src/posts/dto/update-posts.dto.ts b/nestjs-mongoose/src/posts/dto/update-posts.dto.ts deleted file mode 100644 index 1313971..0000000 --- a/nestjs-mongoose/src/posts/dto/update-posts.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Expose, Transform, Exclude } from '@nestjs/class-transformer'; - -export class UpdatePostsDto { - @Exclude() - id: string; - - @Expose() - title: string; - - @Expose() - @Transform(({ value }) => value || 'No-content') - content: string; - - @Expose() - createdAt: Date; -} diff --git a/nestjs-mongoose/src/posts/posts.controller.ts b/nestjs-mongoose/src/posts/posts.controller.ts index 25250c0..5e914f6 100644 --- a/nestjs-mongoose/src/posts/posts.controller.ts +++ b/nestjs-mongoose/src/posts/posts.controller.ts @@ -11,16 +11,17 @@ import { Put, Query, } from '@nestjs/common'; -import CreatePostsDto from './dto/create-posts.dto'; +import CreatePostDto from './dto/create-post.dto'; import ParamsWithId from '../utils/paramsWithId'; -import UpdatePostDto from './dto/update-post.dto'; -import { UpdatePostsDto } from './dto/update-posts.dto'; +import { PatchPostDto } from './dto/patch-post.dto'; import { TransformPlainToClass } from '@nestjs/class-transformer'; -import { GetPostsDto, PostsDto } from './dto/posts.dto'; +import { PostDto } from './dto/post.dto'; import { ApiBody, ApiTags } from '@nestjs/swagger'; import { swaggerConfig } from '../swagger.config'; import { TransformPaginationResponse } from '../common/pagination/transform-pagination-response.decorator'; +import UpdatePostDto from './dto/update-post.dto'; +import { GetPostsDto } from './dto/get-posts.dto'; @ApiTags('Posts') @Controller('posts') @@ -28,23 +29,23 @@ export default class PostsController { constructor(private readonly postsService: PostsService) { } @Get() - @TransformPaginationResponse(PostsDto, { excludeExtraneousValues: true }) + @TransformPaginationResponse(PostDto, { excludeExtraneousValues: true }) async getAllPosts(@Query() query: GetPostsDto) { return this.postsService.findAll(query); } @swaggerConfig.param.id @Get(':id') - @TransformPlainToClass(PostsDto, { excludeExtraneousValues: true }) + @TransformPlainToClass(PostDto, { excludeExtraneousValues: true }) async getPostById(@Param() { id }: ParamsWithId) { return this.postsService.findOne(id); } @Post() - @ApiBody({ type: CreatePostsDto }) + @ApiBody({ type: CreatePostDto }) @ApiBody(swaggerConfig.body) - @TransformPlainToClass(PostsDto, { excludeExtraneousValues: true }) - async createPost(@Body() post: CreatePostsDto) { + @TransformPlainToClass(PostDto, { excludeExtraneousValues: true }) + async createPost(@Body() post: CreatePostDto) { return this.postsService.create(post); } @@ -57,10 +58,10 @@ export default class PostsController { @swaggerConfig.param.id @ApiBody(swaggerConfig.body) @Put(':id') - @TransformPlainToClass(UpdatePostsDto, { excludeExtraneousValues: true }) + @TransformPlainToClass(PostDto, { excludeExtraneousValues: true }) async updatePost( @Param() { id }: ParamsWithId, - @Body() post: CreatePostsDto, + @Body() post: UpdatePostDto, ) { return this.postsService.update(id, post); } @@ -68,10 +69,10 @@ export default class PostsController { @swaggerConfig.param.id @ApiBody(swaggerConfig.body) @Patch(':id') - @TransformPlainToClass(UpdatePostsDto, { excludeExtraneousValues: true }) + @TransformPlainToClass(PostDto, { excludeExtraneousValues: true }) async partialUpdatePost( @Param() { id }: ParamsWithId, - @Body() post: UpdatePostDto, + @Body() post: PatchPostDto, ) { return this.postsService.partialUpdate(id, post); } diff --git a/nestjs-mongoose/src/posts/posts.service.spec.ts b/nestjs-mongoose/src/posts/posts.service.spec.ts index 9356d3f..93c39ed 100644 --- a/nestjs-mongoose/src/posts/posts.service.spec.ts +++ b/nestjs-mongoose/src/posts/posts.service.spec.ts @@ -2,7 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { getModelToken, MongooseModule } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { NotFoundException } from '@nestjs/common'; -import CreatePostsDto from './dto/create-posts.dto'; +import CreatePostDto from './dto/create-post.dto'; import { Post, PostDocument, postForFeature } from './post.schema'; import PostsService from './posts.service'; import { PaginationParamsDto } from '../common/pagination/pagination.dto'; @@ -44,9 +44,9 @@ describe('PostsService', () => { it('should return paginated posts', async () => { const posts: Partial[] = []; for (let i = 0; i < 20; i++) { - const postData: CreatePostsDto = { title: `Post ${i}`, content: `Content ${i}` }; + const postData: CreatePostDto = { title: `Post ${i}`, content: `Content ${i}` }; const post = await model.create(postData); - posts.push({ id: post._id, ...postData }); + posts.push({ id: post.id, ...postData }); } const paginationParams: PaginationParamsDto = { @@ -65,29 +65,102 @@ describe('PostsService', () => { meta: expectedMeta, }; - const result = await service.findAll(paginationParams); - expect(result).toEqual(expectedResult); + const { meta, data } = await service.findAll(paginationParams); + const resultData = data.map(({ id, title, content }) => ({ id: id, title, content })); + expect({ meta, data: resultData }).toEqual(expectedResult); }); }); describe('findOne', () => { it('should return the specified post', async () => { const post = await model.create({ title: 'Post Title', content: 'Post Content' }); - const result = await service.findOne(post._id); - expect(result).toEqual(post); + const result = await service.findOne(post.id); + expect(result.toObject()).toEqual(post.toObject()); }); it('should throw a NotFoundException if the post is not found', async () => { - await expect(service.findOne('invalid-id')).rejects.toThrow(NotFoundException); + await expect(service.findOne(new mongoose.Types.ObjectId().toString())).rejects.toThrow(NotFoundException); }); }); describe('create', () => { it('should create and return the new post', async () => { - const postData: CreatePostsDto = { title: 'New Post Title', content: 'New Post Content' }; + const postData: CreatePostDto = { title: 'New Post Title', content: 'New Post Content' }; const result = await service.create(postData); expect(result).toMatchObject(postData); }); }); -}) \ No newline at end of file + describe('update', () => { + it('should update the specified post', async () => { + // Create a new post + const post = await service.create({ + title: 'Test Post', + content: 'This is a test post.', + }); + + const update = { + title: 'Updated Test Post', + content: 'This is an updated test post.', + } + + // Update the post + await service.update(post.id, update); + + const updatedPost = await service.findOne(post.id); + + // Check that the post was updated + expect({ + id: updatedPost.id, + title: updatedPost.title, + content: updatedPost.content, + }).toEqual({ + id: post.id, + ...update, + }); + + // This check are failing because of https://github.com/Automattic/mongoose/issues/9899 + //expect(updatedPost.createdAt.getTime()).toStrictEqual(post.createdAt.getTime()); + //expect(updatedPost.updatedAt.getTime()).toBeGreaterThan(updatedPost.createdAt.getTime()); + + }); + }); + + describe('partialUpdate', () => { + it('should update the specified post', async () => { + + const initial = { + title: 'Test Post', + content: 'This is a test post.', + } + + // Create a new post + const post = await service.create(initial); + + const update = { + title: 'Updated Test Post', + } + + // Update the post + await service.partialUpdate(post.id, update); + + const updatedPost = await service.findOne(post.id); + + // Check that the post was updated + expect({ + id: updatedPost.id, + title: updatedPost.title, + content: updatedPost.content, + }).toEqual({ + id: post.id, + ...initial, + ...update, + }); + + expect(updatedPost.createdAt.getTime()).toStrictEqual(post.createdAt.getTime()); + expect(updatedPost.updatedAt.getTime()).toBeGreaterThan(updatedPost.createdAt.getTime()); + + }); + }); +}); + diff --git a/nestjs-mongoose/src/posts/posts.service.ts b/nestjs-mongoose/src/posts/posts.service.ts index a9bf81e..b2559dc 100644 --- a/nestjs-mongoose/src/posts/posts.service.ts +++ b/nestjs-mongoose/src/posts/posts.service.ts @@ -1,20 +1,22 @@ import { Model } from 'mongoose'; import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import CreatePostsDto from './dto/create-posts.dto'; +import CreatePostDto from './dto/create-post.dto'; import { Post, PostDocument } from './post.schema'; import { paginate } from '../common/pagination/pagination-mongoose.helper'; -import { PaginationParamsDto } from '../common/pagination/pagination.dto'; +import { DataWithPaginationDto, PaginationParamsDto } from '../common/pagination/pagination.dto'; +import PatchPostDto from './dto/patch-post.dto'; +import UpdatePostDto from './dto/update-post.dto'; @Injectable() class PostsService { constructor(@InjectModel(Post.name) private postModel: Model) { } - async findAll(paginationParams: PaginationParamsDto) { + async findAll(paginationParams: PaginationParamsDto): Promise> { return paginate(this.postModel.find(), paginationParams); } - async findOne(id: string) { + async findOne(id: string): Promise { const post = await this.postModel.findById(id); if (!post) { throw new NotFoundException(); @@ -22,12 +24,12 @@ class PostsService { return post; } - create(postData: CreatePostsDto) { + create(postData: CreatePostDto): Promise { const createdPost = new this.postModel(postData); return createdPost.save(); } - async update(id: string, postData: CreatePostsDto) { + async update(id: string, postData: UpdatePostDto): Promise { const post = await this.postModel .findByIdAndUpdate(id, postData) .setOptions({ overwrite: true, new: true }); @@ -37,7 +39,7 @@ class PostsService { return post; } - async partialUpdate(id: string, postData: CreatePostsDto) { + async partialUpdate(id: string, postData: PatchPostDto): Promise { const post = await this.postModel .findByIdAndUpdate(id, postData) .setOptions({ new: true }); @@ -47,7 +49,7 @@ class PostsService { return post; } - async delete(postId: string) { + async delete(postId: string): Promise { const result = await this.postModel.findByIdAndDelete(postId); if (!result) { throw new NotFoundException(); From 974654448e221d3b38aed89b4f81b3d2743e171e Mon Sep 17 00:00:00 2001 From: gganebnyi Date: Tue, 21 Feb 2023 17:16:16 +0200 Subject: [PATCH 4/4] applied eslint --- nestjs-mongoose/.eslintrc.js | 1 - .../pagination/transform-pagination-response.decorator.ts | 2 +- nestjs-mongoose/src/common/schema/auditable-entity.schema.ts | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/nestjs-mongoose/.eslintrc.js b/nestjs-mongoose/.eslintrc.js index f6c62be..e7bcb53 100644 --- a/nestjs-mongoose/.eslintrc.js +++ b/nestjs-mongoose/.eslintrc.js @@ -7,7 +7,6 @@ module.exports = { plugins: ['@typescript-eslint/eslint-plugin'], extends: [ 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', ], root: true, env: { diff --git a/nestjs-mongoose/src/common/pagination/transform-pagination-response.decorator.ts b/nestjs-mongoose/src/common/pagination/transform-pagination-response.decorator.ts index 3a99620..c1aaccf 100644 --- a/nestjs-mongoose/src/common/pagination/transform-pagination-response.decorator.ts +++ b/nestjs-mongoose/src/common/pagination/transform-pagination-response.decorator.ts @@ -7,7 +7,7 @@ export function TransformPaginationResponse( ): MethodDecorator { return function ( target: Record, - propertyKey: string | Symbol, + propertyKey: string | symbol, descriptor: PropertyDescriptor ): void { const classTransformer: ClassTransformer = new ClassTransformer(); diff --git a/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts b/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts index e4ffebf..976d641 100644 --- a/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts +++ b/nestjs-mongoose/src/common/schema/auditable-entity.schema.ts @@ -7,12 +7,12 @@ export class AuditableEntity { createdAt?: Date; @Prop() - createdBy?: String; + createdBy?: string; @Prop({ default: now() }) updatedAt?: Date; @Prop() - updatedBy?: String; + updatedBy?: string; } \ No newline at end of file