From b29e893fe598f2c3283c69a23276209b98151003 Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 15 Feb 2018 17:28:56 +0800 Subject: [PATCH 01/49] Add TODO --- TODOLIST.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 TODOLIST.md diff --git a/TODOLIST.md b/TODOLIST.md new file mode 100644 index 0000000..8da44d1 --- /dev/null +++ b/TODOLIST.md @@ -0,0 +1,12 @@ +# TODO LIST + +- [ ] 用户组权限 + - [ ] 默认用户组 +- [ ] 上传到指定Categroy +- [ ] 整顿collectin info的goods 列表 +- [ ] Token 使用日志显示 +- [ ] Good 下载次数统计 +- [ ] 接入统计 +- [ ] 配置文件写入初始化用户账号密码 + - [ ] 接入AuthBox +- [ ] 缓存整理 \ No newline at end of file From 8e987cb2d85dc52503a937a2054be196a286b6c4 Mon Sep 17 00:00:00 2001 From: Arylo Date: Sat, 24 Feb 2018 11:36:33 +0800 Subject: [PATCH 02/49] Import `helmet` module --- TODOLIST.md | 7 +++--- package.json | 2 ++ src/express.ts | 2 ++ src/modules/app.module.ts | 4 ++-- .../common/middlewares/clear304.middleware.ts | 22 ------------------- .../common/middlewares/noCache.middleware.ts | 9 ++++++++ 6 files changed, 19 insertions(+), 27 deletions(-) delete mode 100644 src/modules/common/middlewares/clear304.middleware.ts create mode 100644 src/modules/common/middlewares/noCache.middleware.ts diff --git a/TODOLIST.md b/TODOLIST.md index 8da44d1..c8f1a11 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -1,12 +1,13 @@ # TODO LIST -- [ ] 用户组权限 +- [ ] 用户组权限 - [ ] 默认用户组 - [ ] 上传到指定Categroy - [ ] 整顿collectin info的goods 列表 - [ ] Token 使用日志显示 - [ ] Good 下载次数统计 - [ ] 接入统计 -- [ ] 配置文件写入初始化用户账号密码 +- [ ] 配置文件写入初始化用户账号密码 - [ ] 接入AuthBox -- [ ] 缓存整理 \ No newline at end of file +- [ ] 缓存整理 +- [ ] Redis 接入 \ No newline at end of file diff --git a/package.json b/package.json index ebc6842..3e8d5a1 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@types/faker": "^4.1.2", "@types/fs-extra": "^4.0.4", "@types/hasha": "^3.0.0", + "@types/helmet": "0.0.37", "@types/lodash": "^4.14.85", "@types/md5": "^2.1.32", "@types/mocha": "^2.2.44", @@ -78,6 +79,7 @@ "express-session": "^1.15.6", "fs-extra": "^4.0.2", "hasha": "^3.0.0", + "helmet": "^3.11.0", "lodash": "^4.17.4", "md5": "^2.2.1", "md5-file": "^3.2.3", diff --git a/src/express.ts b/src/express.ts index 7800301..1d55582 100644 --- a/src/express.ts +++ b/src/express.ts @@ -2,6 +2,7 @@ import * as express from "express"; import * as session from "express-session"; import * as bodyParser from "body-parser"; import * as cookieParser from "cookie-parser"; +import helmet = require("helmet"); import { error } from "./modules/common/middlewares/logger.middleware"; let server: express.Express; @@ -16,6 +17,7 @@ export const initExpress = () => { mServer.enable("trust proxy"); + mServer.use(helmet()); mServer.use(bodyParser.json()); mServer.use(bodyParser.urlencoded()); mServer.use(cookieParser("storebox")); diff --git a/src/modules/app.module.ts b/src/modules/app.module.ts index 227b62c..5a4e6e7 100644 --- a/src/modules/app.module.ts +++ b/src/modules/app.module.ts @@ -2,7 +2,7 @@ import { Module, MiddlewaresConsumer } from "@nestjs/common"; // Modules import { DatabaseModule } from "./database/database.module"; import {controllers, ControllersModule} from "./controllers.module"; -import { Clear304Middleware } from "./common/middlewares/clear304.middleware"; +import { NoCacheMiddleware } from "./common/middlewares/noCache.middleware"; @Module({ modules: [ DatabaseModule, ControllersModule ] @@ -10,7 +10,7 @@ import { Clear304Middleware } from "./common/middlewares/clear304.middleware"; export class ApplicationModule { public configure(consumer: MiddlewaresConsumer) { consumer - .apply(Clear304Middleware) + .apply(NoCacheMiddleware) .forRoutes(...controllers); } } diff --git a/src/modules/common/middlewares/clear304.middleware.ts b/src/modules/common/middlewares/clear304.middleware.ts deleted file mode 100644 index 1f1f2df..0000000 --- a/src/modules/common/middlewares/clear304.middleware.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ExpressMiddleware, Middleware, NestMiddleware } from "@nestjs/common"; -import { Request, Response, NextFunction } from "express"; - -@Middleware() -export class Clear304Middleware implements NestMiddleware { - public resolve(): ExpressMiddleware { - return (req: Request, res: Response, next: NextFunction) => { - const header = { - // HTTP 1.1. - "Cache-Control": "no-cache, no-store, must-revalidate", - // HTTP 1.0. - "Pragma": "no-cache", - // Proxies. - "Expires": "0" - }; - for (const key of Object.keys(header)) { - res.setHeader(key, header[key]); - } - next(); - }; - } -} diff --git a/src/modules/common/middlewares/noCache.middleware.ts b/src/modules/common/middlewares/noCache.middleware.ts new file mode 100644 index 0000000..ce993f0 --- /dev/null +++ b/src/modules/common/middlewares/noCache.middleware.ts @@ -0,0 +1,9 @@ +import { ExpressMiddleware, Middleware, NestMiddleware } from "@nestjs/common"; +import helmet = require("helmet"); + +@Middleware() +export class NoCacheMiddleware implements NestMiddleware { + public resolve(): ExpressMiddleware { + return helmet.noCache(); + } +} From 892df80ec0b17b1f058f624041003464e0e8b84c Mon Sep 17 00:00:00 2001 From: Arylo Date: Sat, 24 Feb 2018 17:07:53 +0800 Subject: [PATCH 03/49] Support for `redis-session` --- TODOLIST.md | 3 ++- config/app.default.yaml | 4 ++++ docker/app.yaml | 2 ++ docker/docker-compose.yml | 6 ++++++ package.json | 4 +++- src/express.ts | 19 ++++++++++++++++--- src/utils/config.d.ts | 7 ++++++- src/utils/config.ts | 9 +++++++++ 8 files changed, 48 insertions(+), 6 deletions(-) diff --git a/TODOLIST.md b/TODOLIST.md index c8f1a11..ca2065e 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -10,4 +10,5 @@ - [ ] 配置文件写入初始化用户账号密码 - [ ] 接入AuthBox - [ ] 缓存整理 -- [ ] Redis 接入 \ No newline at end of file +- [ ] Redis 接入 + - [x] 支持Session \ No newline at end of file diff --git a/config/app.default.yaml b/config/app.default.yaml index 7946bfa..8ff3ff5 100644 --- a/config/app.default.yaml +++ b/config/app.default.yaml @@ -1,5 +1,9 @@ server: port: 9000 +redis: + host: 127.0.0.1 + port: 6379 + url: db: type: mongo host: 0.0.0.0 diff --git a/docker/app.yaml b/docker/app.yaml index 86bad22..73dad1c 100644 --- a/docker/app.yaml +++ b/docker/app.yaml @@ -1,4 +1,6 @@ db: host: "db" +redis: + host: "cache" path: tmp: /tmp \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 83516fe..8782e58 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -5,6 +5,11 @@ db: container_name: storebox_db restart: always +cache: + image: redis + container_name: storebox_cache + restart: always + svr: build: .. ports: @@ -16,5 +21,6 @@ svr: - ./app.yaml:/usr/src/app/config/app.yaml:ro links: - db + - cache container_name: storebox_svr restart: always \ No newline at end of file diff --git a/package.json b/package.json index 3e8d5a1..70e0fb3 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@nestjs/testing": "^4.5.1", "@types/basic-auth": "^1.1.2", "@types/bunyan": "^1.8.4", + "@types/connect-redis": "0.0.7", "@types/cors": "^2.8.3", "@types/express": "^4.0.39", "@types/express-session": "^1.15.6", @@ -43,6 +44,7 @@ "@types/mocha-steps": "^1.1.0", "@types/mongoose": "^4.7.26", "@types/multer": "^1.3.6", + "@types/node": "^9.3.0", "@types/path-exists": "^3.0.0", "@types/reflect-metadata": "0.0.5", "@types/should": "^11.2.0", @@ -68,11 +70,11 @@ "@nestjs/common": "^4.5.1", "@nestjs/core": "^4.5.1", "@nestjs/swagger": "^1.1.3", - "@types/node": "^9.3.0", "basic-auth": "^2.0.0", "body-parser": "^1.18.2", "bunyan": "^1.8.12", "class-validator": "^0.7.3", + "connect-redis": "^3.3.3", "cookie-parser": "^1.4.3", "cors": "^2.8.4", "express": "^4.16.2", diff --git a/src/express.ts b/src/express.ts index 1d55582..8e59413 100644 --- a/src/express.ts +++ b/src/express.ts @@ -1,9 +1,15 @@ import * as express from "express"; -import * as session from "express-session"; +import session = require("express-session"); import * as bodyParser from "body-parser"; import * as cookieParser from "cookie-parser"; +import connectRedis = require("connect-redis"); +import { config } from "@utils/config"; import helmet = require("helmet"); + import { error } from "./modules/common/middlewares/logger.middleware"; +import { isTest } from "./modules/common/helper/env"; + +const RedisStore = connectRedis(session); let server: express.Express; @@ -21,12 +27,19 @@ export const initExpress = () => { mServer.use(bodyParser.json()); mServer.use(bodyParser.urlencoded()); mServer.use(cookieParser("storebox")); - mServer.use(session({ + const sessionOpts = { + store: undefined, secret: "storebox", resave: false, saveUninitialized: true, cookie: { secure: false, maxAge: 7200 * 1000 } - })); + }; + if (!isTest) { + sessionOpts.store = new RedisStore({ + url: config.redis.url + }); + } + mServer.use(session(sessionOpts)); mServer.use(error); server = mServer; diff --git a/src/utils/config.d.ts b/src/utils/config.d.ts index fd629e6..2556f66 100644 --- a/src/utils/config.d.ts +++ b/src/utils/config.d.ts @@ -19,8 +19,13 @@ interface paths { backup: string; } +interface redis { + url: string; +} + export interface ConfigObj { + redis: redis; server: server; db: db; paths: paths; -} \ No newline at end of file +} diff --git a/src/utils/config.ts b/src/utils/config.ts index f4562f2..5f8f5b3 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -25,6 +25,15 @@ for (const item of Object.keys(configModule.paths)) { } } +configModule.redis.url = configModule.redis.url || + `redis://${configModule.redis.host}:${configModule.redis.port}`; +if ( + configModule.redis.url.indexOf("redis://") !== 0 && + configModule.redis.url.indexOf("//") !== 0 +) { + configModule.redis.url = `redis://${configModule.redis.url}`; +} + export const config = configModule.getConfig() as ConfigObj; import { systemLogger } from "../modules/common/helper/log"; From 6d6d0fbd5d99c0c7595bc430c497357ac4c16142 Mon Sep 17 00:00:00 2001 From: Arylo Date: Sat, 24 Feb 2018 17:40:18 +0800 Subject: [PATCH 04/49] Update NestFacory Api --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 54dac33..8b085f1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ import { isDevelopment } from "./modules/common/helper/env"; const bootstrap = async () => { const server = initExpress(); - const app = await NestFactory.create(ApplicationModule, server); + const app = await NestFactory.create(ApplicationModule, server, { }); app.useGlobalPipes(new ValidationPipe()); if (isDevelopment) { From f69cccdeed024cdd830d704bb6a4d9f1676e6dbd Mon Sep 17 00:00:00 2001 From: Arylo Date: Sun, 25 Feb 2018 17:41:38 +0800 Subject: [PATCH 05/49] Update Dockerfile --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9e5abd4..a22e20c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,10 @@ RUN npm run tsc && \ rm -rf node_modules && \ npm install --production && \ npm install cross-env tsconfig-paths --save-dev && \ + npm install pm2 --global && \ npm cache clean -f -CMD [ "npm", "run", "start:prod" ] +CMD pm2 start index.js --node-args="-r tsconfig-paths/register" -i 0 --no-daemon +# CMD [ "npm", "run", "start:prod" ] EXPOSE 9000 From 98cabba11e06632c4c45634ded853e32fa4f3fbe Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Feb 2018 17:19:23 +0800 Subject: [PATCH 06/49] Add Regexp Service --- TODOLIST.md | 1 + src/models/Regexp.ts | 11 +- .../common/services/regexps.service.ts | 175 ++++++++++++++++++ 3 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 src/modules/common/services/regexps.service.ts diff --git a/TODOLIST.md b/TODOLIST.md index ca2065e..fa20412 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -3,6 +3,7 @@ - [ ] 用户组权限 - [ ] 默认用户组 - [ ] 上传到指定Categroy + - [ ] 上传时追加Catogroy - [ ] 整顿collectin info的goods 列表 - [ ] Token 使用日志显示 - [ ] Good 下载次数统计 diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index 43eb997..820df80 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -15,13 +15,15 @@ const Definition: SchemaDefinition = { link: { type: SchemaTypes.ObjectId, ref: CF - } + }, + hidden: { type: Boolean, default: false } }; export interface IRegexp extends IDocRaw { name: string; value: string; link: ObjectId | ICategory; + hidden: boolean; } export interface IRegexpsRaw extends IRegexp { @@ -52,10 +54,7 @@ RegexpSchema.static( if (link) { obj.link = link; } - return Model.create(obj).then((result) => { - cache.clear(); - return result; - }); + return Model.create(obj); } ); @@ -95,7 +94,7 @@ RegexpSchema.static("discern", (name: string) => { if (cache.get(FLAG_DISCER_LIST)) { p = cache.get(FLAG_DISCER_LIST); } else { - p = Model.find({ link: { $exists: true } }) + p = Model.find({ link: { $exists: true }, hidden: false }) .populate("link") .exec(); cache.put(FLAG_DISCER_LIST, p); diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts new file mode 100644 index 0000000..fcd41f2 --- /dev/null +++ b/src/modules/common/services/regexps.service.ts @@ -0,0 +1,175 @@ +import { Component, BadRequestException } from "@nestjs/common"; +import { ObjectId } from "@models/common"; +import { Model as RegexpsModel, cache, RegexpDoc } from "@models/Regexp"; +import { Model as CategroiesModel, ICategory } from "@models/Categroy"; +import { DEF_PER_COUNT } from "@dtos/page"; + +interface IGetRegexpsOptions { + categroies?: ObjectId[]; + appends?: ObjectId[]; +} + +@Component() +export class RegexpsService { + + private loadAndCache(FLAG: string, value: any, time?: number | string) { + if (!cache.get(FLAG)) { + cache.put(FLAG, value, time); + } + return cache.get(FLAG); + } + + /** + * 新增规则 + */ + public async create(obj) { + try { + return await RegexpsModel.create(obj); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + /** + * 修改规则 + */ + public async editById(id: ObjectId, obj) { + try { + return await RegexpsModel + .findByIdAndUpdate(id, obj, { runValidators: true }) + .exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + /** + * 删除规则 + */ + public async remove(id: ObjectId) { + try { + return await RegexpsModel.findByIdAndRemove(id).exec(); + } catch (error) { + throw new BadRequestException(error.toSrting()); + } + } + + /** + * 规则关联 + * @return {Promise} + */ + public async link(id: ObjectId, linkId?: ObjectId) { + if (!linkId) { + try { + return await RegexpsModel.findByIdAndUpdate(id, { + "$unset": { link: 0 } + }).exec(); + } catch (error) { + throw new BadRequestException(error.toSrting()); + } + } + if (!(await CategroiesModel.findById(linkId).exec())) { + throw new BadRequestException("Nonexist Categroy ID"); + } + try { + return await RegexpsModel.findByIdAndUpdate( + id, { link: linkId }, { runValidators: true } + ).exec(); + } catch (error) { + throw new BadRequestException(error.toSrting()); + } + } + + public async pageCount(perNum = 1): Promise { + const FLAG = `pageCount_${perNum}`; + return this.loadAndCache( + FLAG, + Math.ceil((await this.count()) / perNum), + 3000 + ); + } + + public count(): Promise { + const FLAG = "totalCount"; + return this.loadAndCache( + FLAG, + CategroiesModel.count({ }).exec(), + 3000 + ); + } + + /** + * 规则列表 + * @param perNum {number} 每页数量 + * @param page {number} 页数 + * @return {Promise} + */ + public async list(perNum = DEF_PER_COUNT, page = 1): Promise { + const FLAG = `list_${perNum}_${page}`; + return this.loadAndCache( + FLAG, + RegexpsModel.find({ }) + .skip((page - 1) * perNum).limit(perNum) + .populate("link").exec(), + 3000 + ); + } + + private async getRegexps(opts: IGetRegexpsOptions): Promise { + const DEF_CONDITIONS = { link: { $exists: true }, hidden: false }; + if (opts.categroies && opts.categroies.length > 0) { + // 指定Categroy + const FLAG = `categroies_scan_regexps_${opts.categroies.join("_")}`; + const conditions = { + $or: opts.categroies.reduce((arr, item) => { + arr.push({ link: item }); + return arr; + }, [ ]) + }; + return this.loadAndCache( + FLAG, + RegexpsModel.find(conditions).populate("link").exec(), + 3000 + ); + } else if (opts.appends && opts.appends.length > 0) { + // 追加Categroy + const FLAG = `appends_scan_regexps_${opts.appends.join("_")}`; + const conditions = { + $or: opts.appends.reduce((arr: any, item) => { + arr.push({ link: item }); + return arr; + }, [ DEF_CONDITIONS ]) + }; + return this.loadAndCache( + FLAG, + RegexpsModel.find(conditions).populate("link").exec(), + 3000 + ); + } else { + const FLAG = "default_scan_regexps"; + return this.loadAndCache( + FLAG, + RegexpsModel.find(DEF_CONDITIONS).populate("link").exec(), + 3000 + ); + } + } + + /** + * 根据规则进行识别 + * @return {Promise} + */ + public async discern(name: string, opts: IGetRegexpsOptions) { + const result = await this.getRegexps(opts); + const list = [ ]; + result.forEach((item) => { + const obj = item.toObject(); + const reg = new RegExp(obj.value); + if (reg.test(name)) { + list.push(obj.link); + } + }); + return list as ICategory[]; + } + +} From 896939fd6b915c6fe350d306ae4200a04e3537fb Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Feb 2018 18:05:24 +0800 Subject: [PATCH 07/49] Regexp Service replace Regexp Model Method --- .../common/services/regexps.service.ts | 13 +++-- src/modules/controllers.module.ts | 7 ++- src/modules/regexps/regexps.controller.ts | 49 +++++++------------ test/api/regexps.e2e.ts | 13 +++-- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index fcd41f2..cb9c23d 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -3,6 +3,7 @@ import { ObjectId } from "@models/common"; import { Model as RegexpsModel, cache, RegexpDoc } from "@models/Regexp"; import { Model as CategroiesModel, ICategory } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; +import { isUndefined } from "util"; interface IGetRegexpsOptions { categroies?: ObjectId[]; @@ -13,7 +14,7 @@ interface IGetRegexpsOptions { export class RegexpsService { private loadAndCache(FLAG: string, value: any, time?: number | string) { - if (!cache.get(FLAG)) { + if (cache.get(FLAG) === null) { cache.put(FLAG, value, time); } return cache.get(FLAG); @@ -22,7 +23,7 @@ export class RegexpsService { /** * 新增规则 */ - public async create(obj) { + public async create(obj: object) { try { return await RegexpsModel.create(obj); } catch (error) { @@ -93,11 +94,17 @@ export class RegexpsService { const FLAG = "totalCount"; return this.loadAndCache( FLAG, - CategroiesModel.count({ }).exec(), + RegexpsModel.count({ }).exec(), 3000 ); } + public getRegexp(id: ObjectId) { + return RegexpsModel.findById(id) + .populate({ path: "link", populate: { path: "pid" } }) + .exec(); + } + /** * 规则列表 * @param perNum {number} 每页数量 diff --git a/src/modules/controllers.module.ts b/src/modules/controllers.module.ts index fd4cf44..1d6f852 100644 --- a/src/modules/controllers.module.ts +++ b/src/modules/controllers.module.ts @@ -30,6 +30,7 @@ import { // endregion Middlewares // region Services +import { RegexpsService } from "@services/regexps"; import { CollectionsService } from "@services/collections"; import { UsersService } from "@services/users"; import { TokensService } from "@services/tokens"; @@ -43,9 +44,13 @@ export const controllers = [ CollectionsController, CollectionsAdminController ]; +const services = [ + RegexpsService, CollectionsService, TokensService, UsersService +]; + @Module({ controllers, - components: [ CollectionsService, TokensService, UsersService ] + components: [ ...services ] }) export class ControllersModule { private uploadFileMethod = { diff --git a/src/modules/regexps/regexps.controller.ts b/src/modules/regexps/regexps.controller.ts index 7307892..f68353a 100644 --- a/src/modules/regexps/regexps.controller.ts +++ b/src/modules/regexps/regexps.controller.ts @@ -5,7 +5,7 @@ import { import { ApiBearerAuth, ApiUseTags, ApiResponse, ApiOperation, ApiImplicitParam } from "@nestjs/swagger"; -import { Model as RegexpsModel, IRegexp, RegexpDoc } from "@models/Regexp"; +import { RegexpDoc } from "@models/Regexp"; import { NewRegexp, EditRegexpDot, EditRegexpRawDot } from "./regexps.dto"; @@ -14,6 +14,7 @@ import { RolesGuard } from "@guards/roles"; import { PerPageDto, ListResponse } from "@dtos/page"; import { RidDto } from "@dtos/ids"; import { ParseIntPipe } from "@pipes/parse-int"; +import { RegexpsService } from "@services/regexps"; @UseGuards(RolesGuard) @Controller("api/v1/regexps") @@ -23,6 +24,10 @@ import { ParseIntPipe } from "@pipes/parse-int"; // endregion Swagger Docs export class RegexpsAdminController { + constructor( + private readonly regexpsSvr: RegexpsService + ) { } + @Roles("admin") @Get() // region Swagger Docs @@ -35,15 +40,18 @@ export class RegexpsAdminController { // endregion Swagger Docs public async list(@Query(new ParseIntPipe()) query: PerPageDto) { const curPage = query.page || 1; - const totalPages = await RegexpsModel.countRegexps(query.perNum); - const totalCount = await RegexpsModel.countRegexps(); + const totalPages = await this.regexpsSvr.pageCount(query.perNum); + const totalCount = await this.regexpsSvr.count(); - const data = new ListResponse(); + const data = new ListResponse(); data.current = curPage; data.totalPages = totalPages; data.total = totalCount; + if (data.current > data.totalPages) { + data.current = data.totalPages; + } if (totalPages >= curPage) { - data.data = await RegexpsModel.list(query.perNum, query.page); + data.data = await this.regexpsSvr.list(query.perNum, data.current); } return data; } @@ -53,14 +61,7 @@ export class RegexpsAdminController { @HttpCode(HttpStatus.CREATED) @ApiOperation({ title: "Add RegExp" }) public async add(@Body() ctx: NewRegexp) { - let regexp; - try { - regexp = - await RegexpsModel.addRegexp(ctx.name, ctx.value, ctx.link); - } catch (error) { - throw new BadRequestException(error.toString()); - } - return regexp; + return await this.regexpsSvr.create(ctx); } @Roles("admin") @@ -70,9 +71,7 @@ export class RegexpsAdminController { @ApiOperation({ title: "Get RegExp Info" }) // endregion Swagger Docs public getRegexp(@Param() param: RidDto) { - return RegexpsModel.findById(param.rid) - .populate({ path: "link", populate: { path: "pid" } }) - .exec(); + return this.regexpsSvr.getRegexp(param.rid); } @Roles("admin") @@ -89,15 +88,9 @@ export class RegexpsAdminController { if (Object.keys(data).length === 0) { throw new BadRequestException("No Params"); } - try { - const regexp = await RegexpsModel.findByIdAndUpdate( - param.rid, data, { runValidators: true } - ).exec(); - if (!regexp) { - throw new BadRequestException("NonExist RegExp"); - } - } catch (error) { - throw new BadRequestException(error.toString()); + const regexp = await this.regexpsSvr.editById(param.rid, data); + if (!regexp) { + throw new BadRequestException("NonExist RegExp"); } return { statusCode: HttpStatus.OK }; } @@ -125,11 +118,7 @@ export class RegexpsAdminController { }) // endregion Swagger Docs public async deleteByGet(@Param() param: RidDto) { - try { - await RegexpsModel.removeRegexp(param.rid); - } catch (error) { - throw new BadRequestException(error.toString()); - } + await this.regexpsSvr.remove(param.rid); return { statusCode: HttpStatus.OK }; } diff --git a/test/api/regexps.e2e.ts b/test/api/regexps.e2e.ts index 1d854ce..d45101a 100644 --- a/test/api/regexps.e2e.ts +++ b/test/api/regexps.e2e.ts @@ -2,6 +2,7 @@ import supertest = require("supertest"); import faker = require("faker"); import { Model as RegexpsModel } from "@models/Regexp"; +import { RegexpsService } from "@services/regexps"; import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; import { sleep } from "../helpers/utils"; @@ -10,6 +11,7 @@ describe("Regexp E2E Api", () => { const URL = "/api/v1/regexps"; let request: supertest.SuperTest; + let regexpsSvr: RegexpsService; before(() => { return connect(); @@ -28,6 +30,10 @@ describe("Regexp E2E Api", () => { request = await init(); }); + before(() => { + regexpsSvr = new RegexpsService(); + }); + const user = { name: faker.name.firstName(), pass: faker.random.words() @@ -53,9 +59,10 @@ describe("Regexp E2E Api", () => { value: "^list.2" }]; for (const item of items) { - const doc = await RegexpsModel.addRegexp( - item.name + Date.now(), item.value + Date.now() - ); + const doc = await regexpsSvr.create({ + name: item.name, + value: item.value + Date.now() + }); ids.regexps.push(doc._id); await sleep(100); } From 414409533e9bc3ce73e42455763f1902a776f24b Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 28 Feb 2018 15:05:24 +0800 Subject: [PATCH 08/49] Add Usergroup Service --- TODOLIST.md | 7 +- src/models/User-Usergroup.ts | 70 ++++++++++++ src/models/Usergroup.ts | 36 ++++++ .../common/services/usergroups.service.ts | 105 ++++++++++++++++++ src/modules/database/database.providers.ts | 29 ++++- 5 files changed, 238 insertions(+), 9 deletions(-) create mode 100644 src/models/User-Usergroup.ts create mode 100644 src/models/Usergroup.ts create mode 100644 src/modules/common/services/usergroups.service.ts diff --git a/TODOLIST.md b/TODOLIST.md index ca2065e..a5fff82 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -1,6 +1,7 @@ # TODO LIST -- [ ] 用户组权限 +- [ ] 用户组 + - [ ] 权限 - [ ] 默认用户组 - [ ] 上传到指定Categroy - [ ] 整顿collectin info的goods 列表 @@ -9,6 +10,6 @@ - [ ] 接入统计 - [ ] 配置文件写入初始化用户账号密码 - [ ] 接入AuthBox -- [ ] 缓存整理 - [ ] Redis 接入 - - [x] 支持Session \ No newline at end of file + - [x] 支持Session + - [ ] 缓存整理 \ No newline at end of file diff --git a/src/models/User-Usergroup.ts b/src/models/User-Usergroup.ts new file mode 100644 index 0000000..8d364b9 --- /dev/null +++ b/src/models/User-Usergroup.ts @@ -0,0 +1,70 @@ +import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; +import { FLAG as UF, IUser } from "@models/User"; +import { FLAG as GF, IUsergroups } from "@models/Usergroup"; +import { ObjectId } from "@models/common"; +import { Base, IDoc, IDocRaw } from "./common"; + +const Definition: SchemaDefinition = { + user: { + type: SchemaTypes.ObjectId, + ref: UF, + index: true, + required: true + }, + usergroup: { + type: SchemaTypes.ObjectId, + ref: GF, + index: true, + required: true + } +}; + +/** + * User-Usergroup Model FLAG + */ +export const FLAG = "user-usergroups"; + +/** + * User-Usergroup Doc Interface + */ +export interface IUserUsergroups extends IDocRaw { + user: ObjectId | IUser; + usergroup: ObjectId | IUsergroups; +} + +/** + * User-Usergroup Raw Doc Interface + */ +export interface IUserUsergroupsRaw extends IUserUsergroups { + user: IUser; + usergroup: IUsergroups; +} + +export type UserUsergroupDoc = IDoc; + +const UserUsergroupsSchema = new Base(Definition).createSchema(); + +// region validators + +UserUsergroupsSchema.path("user").validate({ + isAsync: true, + validator: async function userIdModifyValidator(val, respond) { + if (!this.isNew) { + const id = this.getQuery()._id; + const cur = await Model.findById(id).exec(); + if (cur.toObject().user === val) { + return respond(true); + } + } + const result = await Model.findOne({ user: val }).exec(); + return respond(!result); + }, + message: "The User ID is existed" +}); + +// endregion validators + +/** + * User-Usergroup Model + */ +export const Model: M = model(FLAG, UserUsergroupsSchema); diff --git a/src/models/Usergroup.ts b/src/models/Usergroup.ts new file mode 100644 index 0000000..fe1a7c6 --- /dev/null +++ b/src/models/Usergroup.ts @@ -0,0 +1,36 @@ +import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; +import { FLAG as UF, IUser } from "@models/User"; +import { ObjectId } from "@models/common"; +import { Base, IDoc, IDocRaw } from "./common"; + +const Definition: SchemaDefinition = { + name: { type: String, required: true } +}; + +export const FLAG = "usergroups"; + +export interface IUsergroups extends IDocRaw { + name: string; +} + +export type UsergroupDoc = IDoc; + +const UsergroupsSchema = new Base(Definition).createSchema(); + +UsergroupsSchema.path("name").validate({ + isAsync: true, + validator: async function nameExistValidator(val, respond) { + if (!this.isNew) { + const id = this.getQuery()._id; + const ug = await Model.findById(id).exec(); + if (ug.toObject().user === val) { + return respond(true); + } + } + const ug = await Model.findOne({ name: val }).exec(); + return respond(!ug); + }, + message: "The Name is exist" +}); + +export const Model: M = model(FLAG, UsergroupsSchema); diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts new file mode 100644 index 0000000..16d2839 --- /dev/null +++ b/src/modules/common/services/usergroups.service.ts @@ -0,0 +1,105 @@ +import { Component, BadRequestException } from "@nestjs/common"; +import { Model as UsergroupsModel } from "@models/Usergroup"; +import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; +import { ObjectId } from "@models/common"; +import { DEF_PER_COUNT, IPerPage } from "@dtos/page"; + +@Component() +export class UsergroupsService { + + private DEF_PER_OBJ: IPerPage = { + perNum: DEF_PER_COUNT, + page: 1 + }; + + public async add(obj: object) { + try { + return await UsergroupsModel.create(obj); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public async edit(id: ObjectId, obj: object) { + try { + return await UsergroupsModel.update( + { _id: id }, obj, { runValidators: true, context: "query" } + ).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public usersCount(gid: ObjectId) { + return UserUsergroupsModel.count({ usergroup: gid }).exec(); + } + + public async usersCountPage(id: ObjectId, perNum = DEF_PER_COUNT) { + const total = await this.usersCount(id); + return Math.ceil(total / perNum); + } + + public getGroup(gid: ObjectId) { + return UsergroupsModel.findById(gid).exec(); + } + + public async getGroupUsers( + gid: ObjectId, pageObj: IPerPage = this.DEF_PER_OBJ + ) { + const perNum = pageObj.perNum; + const page = pageObj.page; + return (await UserUsergroupsModel.find({ usergroup: gid }) + .skip((page - 1) * perNum).limit(perNum) + .populate("user").exec() + ).map((item) => { + return item.toObject().user; + }); + } + + public count() { + return UsergroupsModel.count({ }).exec(); + } + + public async countPage(perNum = DEF_PER_COUNT) { + const total = await this.count(); + return Math.ceil(total / perNum); + } + + public list(pageObj: IPerPage = this.DEF_PER_OBJ) { + const perNum = pageObj.perNum; + const page = pageObj.page; + return UsergroupsModel.find({ }) + .skip((page - 1) * perNum).limit(perNum) + .sort({ createdAt: -1 }) + .exec(); + } + + public async remove(id: ObjectId) { + try { + return await UsergroupsModel.findByIdAndRemove(id).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public async addUserToGroup(id: ObjectId, uid: ObjectId) { + try { + await UserUsergroupsModel.create({ + user: uid, usergroup: id + }); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public async removeUserToGroup(id: ObjectId, uid: ObjectId) { + try { + await UserUsergroupsModel.findOneAndRemove({ + user: uid, usergroup: id + }).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + +} diff --git a/src/modules/database/database.providers.ts b/src/modules/database/database.providers.ts index d495479..55cb117 100644 --- a/src/modules/database/database.providers.ts +++ b/src/modules/database/database.providers.ts @@ -2,6 +2,8 @@ import * as mongoose from "mongoose"; import { isArray } from "util"; import { config } from "@utils/config"; import { Model as UsersModel } from "@models/User"; +import { Model as UsergroupsModel } from "@models/Usergroup"; +import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { systemLogger } from "../common/helper/log"; const getDatabaseUrl = () => { @@ -21,21 +23,36 @@ export const connectDatabase = () => { return new Promise((resolve, reject) => { const connection = mongoose.connect(getDatabaseUrl(), { useMongoClient: true, - }, (err) => { + }, async (err) => { if (err) { return reject(err); } systemLogger.info("Connected Database."); - UsersModel.count({ }).exec().then((num) => { - if (num === 0) { - return UsersModel.addUser("root", "admin"); - } - }); + await injectData(); return resolve(connection); }); }); }; +export const injectData = async () => { + let num = await UsersModel.count({ }).exec(); + if (num === 0) { + return UsersModel.addUser("root", "admin"); + } + num = await UsergroupsModel.count({ }).exec(); + if (num === 0) { + const group = await UsergroupsModel.create({ name: "admin" }); + const conditions = (await UsersModel.find({ }).exec()) + .map((item) => { + return { + user: item._id, + usergroup: group._id + }; + }); + await UserUsergroupsModel.create(conditions); + } +}; + export const databaseProviders = [ { provide: "DbConnectionToken", From e74ea8b6fc2b1db0b71feaaeae6a225ab5ec877c Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 1 Mar 2018 14:58:34 +0800 Subject: [PATCH 09/49] Add Usergroup apis --- src/modules/common/dtos/ids.dto.ts | 6 + .../common/services/usergroups.service.ts | 19 ++- src/modules/common/services/users.service.ts | 12 ++ src/modules/controllers.module.ts | 12 +- .../usergroups/usergroups.controller.ts | 130 ++++++++++++++++++ src/modules/usergroups/usergroups.dto.ts | 15 ++ src/modules/users/users.controller.ts | 38 ++++- src/modules/users/users.dto.ts | 10 ++ 8 files changed, 232 insertions(+), 10 deletions(-) create mode 100644 src/modules/usergroups/usergroups.controller.ts create mode 100644 src/modules/usergroups/usergroups.dto.ts diff --git a/src/modules/common/dtos/ids.dto.ts b/src/modules/common/dtos/ids.dto.ts index ce69680..02ee5ff 100644 --- a/src/modules/common/dtos/ids.dto.ts +++ b/src/modules/common/dtos/ids.dto.ts @@ -79,6 +79,12 @@ export class GidDto implements IGidDto { public readonly gid: ObjectId; } +export class UGidDto implements IGidDto { + @ApiModelProperty({ type: String, description: "Usergroup ID" }) + @IsMongoId() + public readonly gid: ObjectId; +} + export interface IUidDto { /** * User MongoID diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index 16d2839..78c20e5 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -74,28 +74,35 @@ export class UsergroupsService { .exec(); } - public async remove(id: ObjectId) { + public async remove(gid: ObjectId) { + if ((await this.count()) === 1) { + throw new BadRequestException("Nnn delete unique group"); + } try { - return await UsergroupsModel.findByIdAndRemove(id).exec(); + return await UsergroupsModel.findByIdAndRemove(gid).exec(); } catch (error) { throw new BadRequestException(error.toString()); } } - public async addUserToGroup(id: ObjectId, uid: ObjectId) { + public async addUserToGroup(gid: ObjectId, uid: ObjectId) { + // TODO User Check + // TODO Usergroup Check try { await UserUsergroupsModel.create({ - user: uid, usergroup: id + user: uid, usergroup: gid }); } catch (error) { throw new BadRequestException(error.toString()); } } - public async removeUserToGroup(id: ObjectId, uid: ObjectId) { + public async removeUserFromGroup(gid: ObjectId, uid: ObjectId) { + // TODO User Check + // TODO Usergroup Check try { await UserUsergroupsModel.findOneAndRemove({ - user: uid, usergroup: id + user: uid, usergroup: gid }).exec(); } catch (error) { throw new BadRequestException(error.toString()); diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index d9d1c1d..7b247b5 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -1,10 +1,22 @@ import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; import { Model as UsersModel, UserDoc } from "@models/User"; +import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; +import { IUsergroups } from "@models/Usergroup"; @Component() export class UsersService { + public async getUsergroup(uid: ObjectId) { + const group = await UserUsergroupsModel + .findOne({ user: uid }).populate("usergroup") + .exec(); + if (!group) { + return group as undefined; + } + return group.toObject().usergroup as IUsergroups; + } + /** * 修改`User`属性, 除了`username` * @param id User ID diff --git a/src/modules/controllers.module.ts b/src/modules/controllers.module.ts index fd4cf44..b20dcd0 100644 --- a/src/modules/controllers.module.ts +++ b/src/modules/controllers.module.ts @@ -14,6 +14,7 @@ import { CollectionsAdminController } from "./collections/collections.admin.controller"; import { TokensAdminController } from "./tokens/tokens.controller"; +import { UsergroupsAdminController } from "./usergroups/usergroups.controller"; // endregion Controllers // region Middlewares @@ -33,19 +34,26 @@ import { import { CollectionsService } from "@services/collections"; import { UsersService } from "@services/users"; import { TokensService } from "@services/tokens"; +import { UsergroupsService } from "@services/usergroups"; // endregion Services export const controllers = [ FilesController, GoodsController, - UsersAdminController, AuthAdminController, RegexpsAdminController, + UsersAdminController, AuthAdminController, + UsergroupsAdminController, + RegexpsAdminController, CategoriesAdminController, GoodsAdminController, TokensAdminController, CollectionsController, CollectionsAdminController ]; +export const services = [ + CollectionsService, TokensService, UsersService, UsergroupsService +]; + @Module({ controllers, - components: [ CollectionsService, TokensService, UsersService ] + components: [ ...services ] }) export class ControllersModule { private uploadFileMethod = { diff --git a/src/modules/usergroups/usergroups.controller.ts b/src/modules/usergroups/usergroups.controller.ts new file mode 100644 index 0000000..6ff40d2 --- /dev/null +++ b/src/modules/usergroups/usergroups.controller.ts @@ -0,0 +1,130 @@ +import { UseGuards, Controller, Get, Query, HttpStatus, HttpCode, Post, Body, Param, Delete } from "@nestjs/common"; +import { ApiUseTags, ApiOperation, ApiResponse } from "@nestjs/swagger"; +import { RolesGuard } from "@guards/roles"; +import { UsergroupsService } from "@services/usergroups"; +import { Roles } from "@decorators/roles"; +import { ParseIntPipe } from "@pipes/parse-int"; +import { PerPageDto, ListResponse, DEF_PER_COUNT } from "@dtos/page"; +import { UGidDto } from "@dtos/ids"; + +import { AddUsergroupDto, EditUsergroupDto } from "./usergroups.dto"; + +@UseGuards(RolesGuard) +@ApiUseTags("User Groups") +@Controller("api/v1/usergroups") +export class UsergroupsAdminController { + + constructor(private readonly ugSvr: UsergroupsService) { } + + @Roles("admin") + @Get() + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Get Usergroup List" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Usergroup List", + type: ListResponse + }) + // endregion Swagger Docs + public async getList(@Query(new ParseIntPipe()) query: PerPageDto) { + const curPage = query.page || 1; + const perNum = query.perNum || DEF_PER_COUNT; + const resData = new ListResponse(); + resData.current = curPage; + resData.totalPages = await this.ugSvr.countPage(perNum); + resData.total = await this.ugSvr.count(); + if (resData.totalPages >= resData.current) { + resData.data = await this.ugSvr.list({ + page: curPage, perNum + }); + } + return resData; + } + + @Roles("admin") + @Post() + // region Swagger Docs + @HttpCode(HttpStatus.CREATED) + @ApiOperation({ title: "New Usergroup" }) + @ApiResponse({ + status: HttpStatus.CREATED, description: "Success" + }) + // endregion Swagger Docs + public addUsergroup(@Body() body: AddUsergroupDto) { + return this.ugSvr.add(body); + } + + @Roles("admin") + @Get("/:gid/delete") + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Delete Usergroup" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Delete Success" + }) + // endregion Swagger Docs + public async removeUsergroupByGet(@Param() param: UGidDto) { + return this.ugSvr.remove(param.gid); + } + + @Roles("admin") + @Delete("/:gid") + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Delete Usergroup" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Delete Success" + }) + // endregion Swagger Docs + public async removeUsergroupByDelete(@Param() param: UGidDto) { + return this.ugSvr.remove(param.gid); + } + + @Roles("admin") + @Post("/:gid") + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Modify Usergroup Info" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Modify Success" + }) + // endregion Swagger Docs + public async editUsergroup( + @Param() param: UGidDto, @Body() body: EditUsergroupDto + ) { + return this.ugSvr.edit(param.gid, body); + } + + @Roles("admin") + @Get("/:gid") + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Get Usergroup Info" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Usergroup Info" + }) + // endregion Swagger Docs + public async getUsergroup( + @Param() param: UGidDto, @Query(new ParseIntPipe()) query: PerPageDto + ) { + let group: any = await this.ugSvr.getGroup(param.gid); + if (!group) { + return group; + } + group = group.toObject(); + const users = new ListResponse(); + const curPage = query.page || 1; + const perNum = query.perNum || DEF_PER_COUNT; + users.current = curPage; + users.totalPages = await this.ugSvr.usersCountPage(param.gid, perNum); + users.total = await this.ugSvr.usersCount(param.gid); + if (users.totalPages >= users.current) { + users.data = await this.ugSvr.getGroupUsers(param.gid, { + page: curPage, perNum + }); + } + group.users = users; + return group; + } + +} diff --git a/src/modules/usergroups/usergroups.dto.ts b/src/modules/usergroups/usergroups.dto.ts new file mode 100644 index 0000000..d87b6e6 --- /dev/null +++ b/src/modules/usergroups/usergroups.dto.ts @@ -0,0 +1,15 @@ +import { ApiModelProperty } from "@nestjs/swagger"; +import { IsString, IsMongoId } from "class-validator"; +import { ObjectId } from "@models/common"; + +export class AddUsergroupDto { + @ApiModelProperty({ type: String, description: "Usergroup Name" }) + @IsString() + public readonly name: string; +} + +export class EditUsergroupDto { + @ApiModelProperty({ type: String, description: "Usergroup Name" }) + @IsString() + public readonly name: string; +} diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index ecbd06a..796c8fc 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -14,6 +14,7 @@ import { Roles } from "@decorators/roles"; import { RolesGuard } from "@guards/roles"; import { ParseIntPipe } from "@pipes/parse-int"; import { TokensService } from "@services/tokens"; +import { UsergroupsService } from "@services/usergroups"; import { CollectionsService } from "@services/collections"; import { UsersService } from "@services/users"; import { PerPageDto, ListResponse, DEF_PER_COUNT } from "@dtos/page"; @@ -21,7 +22,7 @@ import { UidDto } from "@dtos/ids"; import { DefResDto } from "@dtos/res"; import { - CreateUserDto, ModifyPasswordDto, EditUserDto + CreateUserDto, ModifyPasswordDto, EditUserDto, UserUsergroupParamDto } from "./users.dto"; @UseGuards(RolesGuard) @@ -35,7 +36,8 @@ export class UsersAdminController { constructor( private readonly tokensSvr: TokensService, private readonly collectionsSvr: CollectionsService, - private readonly usersSvr: UsersService + private readonly usersSvr: UsersService, + private readonly ugSvr: UsergroupsService ) { } @Roles("admin") @@ -362,6 +364,38 @@ export class UsersAdminController { // endregion Collection Methods //////////////////////////////////////// + //////////////////////////////////////// + // region Usergroup Methods + //////////////////////////////////////// + + @Roles("admin") + @Get("/:uid/usergroup/:gid") + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Move Usergroup" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Move Success", + }) + // endregion Swagger Docs + public async moveUsergroup(@Param() param: UserUsergroupParamDto) { + const gid = (await this.usersSvr.getUsergroup(param.gid))._id; + if (gid.toString() === param.gid) { + throw new BadRequestException("This is old Usergroup ID"); + } + await this.ugSvr.removeUserFromGroup(gid, param.uid); + try { + await this.ugSvr.addUserToGroup(param.gid, param.uid); + } catch (error) { + await this.ugSvr.addUserToGroup(gid, param.uid); + throw error; + } + return new DefResDto(); + } + + //////////////////////////////////////// + // endregion Usergroup Methods + //////////////////////////////////////// + @Roles("admin") @Get("/:uid") // region Swagger Docs diff --git a/src/modules/users/users.dto.ts b/src/modules/users/users.dto.ts index df44a52..e2f2f25 100644 --- a/src/modules/users/users.dto.ts +++ b/src/modules/users/users.dto.ts @@ -1,6 +1,7 @@ import { IsString, IsMongoId } from "class-validator"; import { ObjectId } from "@models/common"; import { ApiUseTags, ApiModelProperty } from "@nestjs/swagger"; +import { IUidDto, IGidDto } from "@dtos/ids"; export class CreateUserDto { @ApiModelProperty({ type: String, description: "Username" }) @@ -25,3 +26,12 @@ export class ModifyPasswordDto { @IsString() public readonly newPassword: string; } + +export class UserUsergroupParamDto implements IUidDto, IGidDto { + @ApiModelProperty({ type: String, description: "User ID" }) + @IsMongoId() + public readonly uid: ObjectId; + @ApiModelProperty({ type: String, description: "Usergroup ID" }) + @IsMongoId() + public readonly gid: ObjectId; +} From ed1ab5e2168b14170503eb2e9fb3f244c860cf1e Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 1 Mar 2018 18:22:11 +0800 Subject: [PATCH 10/49] Add Test Unit --- .../common/services/usergroups.service.ts | 16 +++ src/modules/common/services/users.service.ts | 2 +- src/modules/users/users.controller.ts | 17 ++- test/api/user_usergroup.e2e.ts | 67 +++++++++++ test/api/usergroups.e2e.ts | 106 ++++++++++++++++++ test/helpers/database.ts | 46 ++++---- test/helpers/database/user.ts | 10 ++ test/helpers/database/usergroups.ts | 29 +++++ 8 files changed, 259 insertions(+), 34 deletions(-) create mode 100644 test/api/user_usergroup.e2e.ts create mode 100644 test/api/usergroups.e2e.ts create mode 100644 test/helpers/database/user.ts create mode 100644 test/helpers/database/usergroups.ts diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index 78c20e5..49a03fc 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -109,4 +109,20 @@ export class UsergroupsService { } } + public async moveUser(gid: ObjectId, uid: ObjectId) { + let doc = null; + try { + doc = await UserUsergroupsModel.update( + { user: uid }, { usergroup: gid }, + { runValidators: true, context: "query" } + ).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + if (!doc) { + throw new BadRequestException("User hasnt usergroup"); + } + return doc; + } + } diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index 7b247b5..dd2cbee 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -12,7 +12,7 @@ export class UsersService { .findOne({ user: uid }).populate("usergroup") .exec(); if (!group) { - return group as undefined; + return null; } return group.toObject().usergroup as IUsergroups; } diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 796c8fc..8edc326 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -378,16 +378,15 @@ export class UsersAdminController { }) // endregion Swagger Docs public async moveUsergroup(@Param() param: UserUsergroupParamDto) { - const gid = (await this.usersSvr.getUsergroup(param.gid))._id; - if (gid.toString() === param.gid) { - throw new BadRequestException("This is old Usergroup ID"); - } - await this.ugSvr.removeUserFromGroup(gid, param.uid); - try { + const group = await this.usersSvr.getUsergroup(param.uid); + if (group) { + const gid = group._id; + if (gid.toString() === param.gid) { + throw new BadRequestException("This is old Usergroup ID"); + } + await this.ugSvr.moveUser(param.gid, param.uid); + } else { await this.ugSvr.addUserToGroup(param.gid, param.uid); - } catch (error) { - await this.ugSvr.addUserToGroup(gid, param.uid); - throw error; } return new DefResDto(); } diff --git a/test/api/user_usergroup.e2e.ts b/test/api/user_usergroup.e2e.ts new file mode 100644 index 0000000..61128e3 --- /dev/null +++ b/test/api/user_usergroup.e2e.ts @@ -0,0 +1,67 @@ +import supertest = require("supertest"); +import faker = require("faker"); + +import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; +import { UsersService } from "@services/users"; + +import { + connect, drop, addCategoryAndRegexp +} from "../helpers/database"; +import { init } from "../helpers/server"; +import { + newUsergroup, getLinkIdByUserId +} from "../helpers/database/usergroups"; +import { newUser } from "../helpers/database/user"; + +describe("User's Usergroup E2E Api", () => { + + let request: supertest.SuperTest; + + before(() => { + return connect(); + }); + + const ids = { + users: [ ], + usergroups: [ ], + userusergroups: [ ] + }; + + after(() => { + return drop(ids); + }); + + before(async () => { + request = await init(); + }); + + let userSvr: UsersService; + before(() => { + userSvr = new UsersService(); + }); + + const user = { + username: `${faker.name.firstName()}${Math.random()}`, + password: faker.random.words() + }; + step("Login", async () => { + const userDoc = await newUser(user.username, user.password); + ids.users.push(userDoc._id); + const groupDoc = await newUsergroup(undefined, userDoc._id); + ids.usergroups.push(groupDoc._id); + ids.userusergroups.push(await getLinkIdByUserId(userDoc._id)); + await request.post("/api/v1/auth/login").send(user).then(); + }); + + step("Move Usergroup", async () => { + const groupDoc = await newUsergroup(); + ids.usergroups.push(groupDoc._id); + const url = `/api/v1/users/${ids.users[0]}/usergroup/${groupDoc._id}`; + const { status, body: result } = await request.get(url).then(); + status.should.be.eql(200); + const group = await userSvr.getUsergroup(ids.users[0]); + group._id.toString().should.be.eql(groupDoc._id.toString()); + group._id.toString().should.be.not.eql(ids.usergroups[0].toString()); + }); + +}); diff --git a/test/api/usergroups.e2e.ts b/test/api/usergroups.e2e.ts new file mode 100644 index 0000000..ba57a35 --- /dev/null +++ b/test/api/usergroups.e2e.ts @@ -0,0 +1,106 @@ +import supertest = require("supertest"); +import faker = require("faker"); + +import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; +import { UsersService } from "@services/users"; + +import { + connect, drop, addCategoryAndRegexp +} from "../helpers/database"; +import { init } from "../helpers/server"; +import { + newUsergroup, getLinkIdByUserId +} from "../helpers/database/usergroups"; +import { newUser } from "../helpers/database/user"; + +describe("Usergroup E2E Api", () => { + + let request: supertest.SuperTest; + + before(() => { + return connect(); + }); + + const ids = { + users: [ ], + usergroups: [ ], + userusergroups: [ ] + }; + + after(() => { + return drop(ids); + }); + + before(async () => { + request = await init(); + }); + + let userSvr: UsersService; + before(() => { + userSvr = new UsersService(); + }); + + const user = { + username: `${faker.name.firstName()}${Math.random()}`, + password: faker.random.words() + }; + step("Login", async () => { + const userDoc = await newUser(user.username, user.password); + ids.users.push(userDoc._id); + const groupDoc = await newUsergroup(undefined, userDoc._id); + ids.usergroups.push(groupDoc._id); + ids.userusergroups.push(await getLinkIdByUserId(userDoc._id)); + await request.post("/api/v1/auth/login").send(user).then(); + }); + + step("New Usergroup * 2", async () => { + for (let i = 0; i < 2; i++) { + const url = `/api/v1/usergroups`; + const name = `${faker.random.word()}${Math.random()}`; + const { status, body: result } = + await request.post(url).send({ name }).then(); + status.should.be.eql(201); + ids.usergroups.push(result._id); + } + }); + + step("Usergroup List", async () => { + const url = `/api/v1/usergroups`; + const { status, body: result } = await request.get(url).then(); + status.should.be.eql(200); + result.total.should.aboveOrEqual(2); + result.data.should.be.an.Array(); + }); + + step("Get Usergroup Info", async () => { + const id = ids.usergroups[ids.usergroups.length - 1]; + const url = `/api/v1/usergroups/${id}`; + const { status, body: result } = await request.get(url).then(); + status.should.be.eql(200); + result.users.data.should.be.an.Array(); + result.users.total.should.be.eql(0); + }); + + step("Modify Usergroup's name", async () => { + const id = ids.usergroups[ids.usergroups.length - 1]; + const url = `/api/v1/usergroups/${id}`; + const name = `${faker.random.word()}${Math.random()}`; + const { status } = await request.post(url).send({ name }).then(); + status.should.be.eql(200); + const { body: result } = await request.get(url).then(); + result.should.have.property("name", name); + }); + + step("Delete By GET", async () => { + const id = ids.usergroups[ids.usergroups.length - 1]; + const url = `/api/v1/usergroups/${id}/delete`; + const { status } = await request.get(url).then(); + }); + + step("Delete By DELETE", async () => { + const id = ids.usergroups[ids.usergroups.length - 2]; + const url = `/api/v1/usergroups/${id}`; + const { status } = await request.delete(url).then(); + }); + +}); diff --git a/test/helpers/database.ts b/test/helpers/database.ts index 393c1a8..f771f77 100644 --- a/test/helpers/database.ts +++ b/test/helpers/database.ts @@ -1,7 +1,6 @@ import faker = require("faker"); import { config } from "@utils/config"; import { ObjectId } from "@models/common"; -import { connectDatabase } from "../../src/modules/database/database.providers"; import { Model as ValuesModel } from "@models/Value"; import { Model as GoodsModels } from "@models/Good"; @@ -10,6 +9,11 @@ import { Model as UsersModel } from "@models/User"; import { Model as TokensModel } from "@models/Token"; import { Model as CollectionsModel } from "@models/Collection"; import { Model as CategoriesModel, CategoryDoc } from "@models/Categroy"; +import { Model as UsergroupsModel } from "@models/Usergroup"; +import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; + +import { connectDatabase } from "../../src/modules/database/database.providers"; +import { newUser as newUserFn } from "./database/user"; config.db.database = "storebox-test"; @@ -37,26 +41,22 @@ export const drop = async (ids?: IIds) => { await CategoriesModel.remove({ }).exec(); return; } - for (const id of (ids.values || [ ])) { - await ValuesModel.findByIdAndRemove(id).exec(); - } - for (const id of (ids.goods || [ ])) { - await GoodsModels.findByIdAndRemove(id).exec(); - } - for (const id of (ids.regexps || [ ])) { - await RegexpsModel.findByIdAndRemove(id).exec(); - } - for (const id of (ids.users || [ ])) { - await UsersModel.findByIdAndRemove(id).exec(); - } - for (const id of (ids.tokens || [ ])) { - await TokensModel.findByIdAndRemove(id).exec(); - } - for (const id of (ids.categories || [ ])) { - await CategoriesModel.findByIdAndRemove(id).exec(); - } - for (const id of (ids.collections || [ ])) { - await CollectionsModel.findByIdAndRemove(id).exec(); + const MODEL_IDMETHOD_MAP = { + "values": ValuesModel, + "goods": GoodsModels, + "regexps": RegexpsModel, + "users": UsersModel, + "tokens": TokensModel, + "categories": CategoriesModel, + "collections": CollectionsModel, + "usergroups": UsergroupsModel, + "userusergroups": UserUsergroupsModel + }; + for (const method of Object.keys(MODEL_IDMETHOD_MAP)) { + const model = MODEL_IDMETHOD_MAP[method]; + for (const id of (ids[method] || [ ])) { + await model.findByIdAndRemove(id).exec(); + } } }; @@ -68,9 +68,7 @@ export const addCategoryAndRegexp = async (regexp: RegExp) => { return [category, reg]; }; -export const newUser = (username: string, password: string) => { - return UsersModel.addUser(username, password); -}; +export const newUser = newUserFn; export const newRegexp = (name: string, value: RegExp, link?) => { return RegexpsModel.addRegexp(name, value.source, link); diff --git a/test/helpers/database/user.ts b/test/helpers/database/user.ts new file mode 100644 index 0000000..a2d6217 --- /dev/null +++ b/test/helpers/database/user.ts @@ -0,0 +1,10 @@ +import { Model as UsersModel } from "@models/User"; +import { ObjectId } from "@models/common"; +import faker = require("faker"); + +export const newUser = ( + username = `${faker.name.firstName()}${Math.random()}`, + password = `${faker.random.words()}${Math.random()}` +) => { + return UsersModel.addUser(username, password); +}; diff --git a/test/helpers/database/usergroups.ts b/test/helpers/database/usergroups.ts new file mode 100644 index 0000000..a61d377 --- /dev/null +++ b/test/helpers/database/usergroups.ts @@ -0,0 +1,29 @@ +import { ObjectId } from "@models/common"; +import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; +import { Model as UsergroupsModel } from "@models/Usergroup"; +import faker = require("faker"); + +/// + +export const getLinkIdByUserId = async (uid: ObjectId) => { + return (await UserUsergroupsModel.findOne({ user: uid }).exec())._id; +}; + +export const getLinkIdsByUsergroupId = async (gid: ObjectId) => { + return (await UserUsergroupsModel.find({ usergroup: gid }).exec()) + .map((item) => { + return item._id; + }); +}; + +export const newUsergroup = async ( + name = `${faker.random.word}${Math.random()}`, uid?: ObjectId +) => { + const group = await UsergroupsModel.create({ name }); + if (uid) { + const link = await UserUsergroupsModel.create({ + user: uid, usergroup: group._id + }); + } + return group; +}; From 41572162a04277732d9baf786b13d6454d6b73dc Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 5 Mar 2018 15:52:23 +0800 Subject: [PATCH 11/49] Add the feature of Default Usergroup ID --- TODOLIST.md | 4 +- src/models/System.ts | 20 ++++++ src/models/User-Usergroup.ts | 20 ------ src/modules/common/services/system.service.ts | 38 +++++++++++ .../common/services/usergroups.service.ts | 16 ----- src/modules/common/services/users.service.ts | 60 +++++++++++++++-- src/modules/controllers.module.ts | 4 +- src/modules/database/database.providers.ts | 6 ++ .../usergroups/usergroups.controller.ts | 28 +++++++- src/modules/usergroups/usergroups.dto.ts | 10 +++ src/modules/users/users.controller.ts | 60 ++++++++--------- src/modules/users/users.dto.ts | 12 ++-- test/api/user_usergroup.e2e.ts | 65 ++++++++++++------- test/api/usergroups.e2e.ts | 11 +--- test/helpers/database.ts | 5 ++ test/helpers/database/user.ts | 12 ++++ test/helpers/database/usergroups.ts | 9 ++- test/issues/ban_user_n_its_token.e2e.ts | 3 +- test/services/users.spec.ts | 3 +- 19 files changed, 265 insertions(+), 121 deletions(-) create mode 100644 src/models/System.ts create mode 100644 src/modules/common/services/system.service.ts diff --git a/TODOLIST.md b/TODOLIST.md index a5fff82..36ce465 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -1,8 +1,8 @@ # TODO LIST -- [ ] 用户组 +- [x] 用户组 - [ ] 权限 - - [ ] 默认用户组 + - [x] 默认用户组 - [ ] 上传到指定Categroy - [ ] 整顿collectin info的goods 列表 - [ ] Token 使用日志显示 diff --git a/src/models/System.ts b/src/models/System.ts new file mode 100644 index 0000000..375b703 --- /dev/null +++ b/src/models/System.ts @@ -0,0 +1,20 @@ +import { model, SchemaDefinition, Model as M } from "mongoose"; +import { Base, IDoc, IDocRaw } from "./common"; + +const Definition: SchemaDefinition = { + key: { type: String, required: true }, + value: { type: String, required: true } +}; + +export interface ISystem extends IDocRaw { + key: string; + value: string; +} + +const SystemSchema = new Base(Definition).createSchema(); + +export const Flag = "sys"; + +export type SystemDoc = IDoc; + +export const Model: M = model(Flag, SystemSchema); diff --git a/src/models/User-Usergroup.ts b/src/models/User-Usergroup.ts index 8d364b9..ae38d5d 100644 --- a/src/models/User-Usergroup.ts +++ b/src/models/User-Usergroup.ts @@ -44,26 +44,6 @@ export type UserUsergroupDoc = IDoc; const UserUsergroupsSchema = new Base(Definition).createSchema(); -// region validators - -UserUsergroupsSchema.path("user").validate({ - isAsync: true, - validator: async function userIdModifyValidator(val, respond) { - if (!this.isNew) { - const id = this.getQuery()._id; - const cur = await Model.findById(id).exec(); - if (cur.toObject().user === val) { - return respond(true); - } - } - const result = await Model.findOne({ user: val }).exec(); - return respond(!result); - }, - message: "The User ID is existed" -}); - -// endregion validators - /** * User-Usergroup Model */ diff --git a/src/modules/common/services/system.service.ts b/src/modules/common/services/system.service.ts new file mode 100644 index 0000000..c131a45 --- /dev/null +++ b/src/modules/common/services/system.service.ts @@ -0,0 +1,38 @@ +import { Component } from "@nestjs/common"; +import { ObjectId } from "@models/common"; +import { Model as SystemModel } from "@models/System"; +import { Model as UsergroupsModel } from "@models/Usergroup"; + +@Component() +export class SystemService { + + public static DEFAULT_USERGROUP_FLAG = "DEFAULT_USERGROUP"; + + /** + * Get Default Usergroup ID + * @returns Usergroup ID + */ + public async getDefaultUsergroup(): Promise { + let gid: any = await SystemModel.findOne({ + key: SystemService.DEFAULT_USERGROUP_FLAG + }).exec(); + if (!gid) { + gid = (await UsergroupsModel.findOne().exec())._id; + this.setDefaultUsergroup(gid); + } + return gid; + } + + /** + * Set Default Usergroup ID + * @param gid Usergroup ID + */ + public setDefaultUsergroup(gid: ObjectId) { + return SystemModel.findOneAndUpdate( + { key: SystemService.DEFAULT_USERGROUP_FLAG }, { value: gid }, + { upsert: true } + ) + .exec(); + } + +} diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index 49a03fc..78c20e5 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -109,20 +109,4 @@ export class UsergroupsService { } } - public async moveUser(gid: ObjectId, uid: ObjectId) { - let doc = null; - try { - doc = await UserUsergroupsModel.update( - { user: uid }, { usergroup: gid }, - { runValidators: true, context: "query" } - ).exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } - if (!doc) { - throw new BadRequestException("User hasnt usergroup"); - } - return doc; - } - } diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index dd2cbee..27bd62a 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -3,18 +3,64 @@ import { ObjectId } from "@models/common"; import { Model as UsersModel, UserDoc } from "@models/User"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { IUsergroups } from "@models/Usergroup"; +import { SystemService } from "@services/system"; +import { IPerPage, DEF_PER_COUNT } from "@dtos/page"; @Component() export class UsersService { - public async getUsergroup(uid: ObjectId) { - const group = await UserUsergroupsModel - .findOne({ user: uid }).populate("usergroup") - .exec(); - if (!group) { - return null; + private DEF_PER_OBJ: IPerPage = { + perNum: DEF_PER_COUNT, + page: 1 + }; + + constructor(private readonly sysSvr: SystemService) { } + + public async addUser(obj, gid?: ObjectId) { + try { + const user = await UsersModel.addUser(obj.username, obj.password); + if (!gid) { + gid = await this.sysSvr.getDefaultUsergroup(); + } + await UserUsergroupsModel.create({ + user: user._id, usergroup: gid + }); + return user; + } catch (error) { + throw new BadRequestException(error.toString()); } - return group.toObject().usergroup as IUsergroups; + } + + public async removeUser(uid: ObjectId) { + try { + await UsersModel.removeUser(uid); + await UserUsergroupsModel.remove({ user: uid }).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public countUsergroups(uid: ObjectId) { + return UserUsergroupsModel.count({ user: uid }).exec(); + } + + public async countPageUsergroups(uid: ObjectId, perNum = DEF_PER_COUNT) { + const total = await this.countUsergroups(uid); + return Math.ceil(total / perNum); + } + + public async getUsergroups( + uid: ObjectId, pageObj: IPerPage = this.DEF_PER_OBJ + ) { + const perNum = pageObj.perNum; + const page = pageObj.page; + const groups = await UserUsergroupsModel + .find({ user: uid }).populate("usergroup") + .skip((page - 1) * perNum).limit(perNum) + .exec(); + return groups.map((item) => { + return item.toObject().usergroup as IUsergroups; + }); } /** diff --git a/src/modules/controllers.module.ts b/src/modules/controllers.module.ts index b20dcd0..7770fe7 100644 --- a/src/modules/controllers.module.ts +++ b/src/modules/controllers.module.ts @@ -35,6 +35,7 @@ import { CollectionsService } from "@services/collections"; import { UsersService } from "@services/users"; import { TokensService } from "@services/tokens"; import { UsergroupsService } from "@services/usergroups"; +import { SystemService } from "@services/system"; // endregion Services export const controllers = [ @@ -48,7 +49,8 @@ export const controllers = [ ]; export const services = [ - CollectionsService, TokensService, UsersService, UsergroupsService + CollectionsService, TokensService, UsersService, UsergroupsService, + SystemService ]; @Module({ diff --git a/src/modules/database/database.providers.ts b/src/modules/database/database.providers.ts index 55cb117..eff2f04 100644 --- a/src/modules/database/database.providers.ts +++ b/src/modules/database/database.providers.ts @@ -4,6 +4,8 @@ import { config } from "@utils/config"; import { Model as UsersModel } from "@models/User"; import { Model as UsergroupsModel } from "@models/Usergroup"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; +import { Model as SystemModel } from "@models/System"; +import { SystemService } from "@services/system"; import { systemLogger } from "../common/helper/log"; const getDatabaseUrl = () => { @@ -49,6 +51,10 @@ export const injectData = async () => { usergroup: group._id }; }); + await SystemModel.findOneAndUpdate( + { key: SystemService.DEFAULT_USERGROUP_FLAG }, + { value: group._id.toString() }, { upsert: true } + ).exec(); await UserUsergroupsModel.create(conditions); } }; diff --git a/src/modules/usergroups/usergroups.controller.ts b/src/modules/usergroups/usergroups.controller.ts index 6ff40d2..187cfac 100644 --- a/src/modules/usergroups/usergroups.controller.ts +++ b/src/modules/usergroups/usergroups.controller.ts @@ -7,7 +7,7 @@ import { ParseIntPipe } from "@pipes/parse-int"; import { PerPageDto, ListResponse, DEF_PER_COUNT } from "@dtos/page"; import { UGidDto } from "@dtos/ids"; -import { AddUsergroupDto, EditUsergroupDto } from "./usergroups.dto"; +import { AddUsergroupDto, EditUsergroupDto, UserUsergroupDto } from "./usergroups.dto"; @UseGuards(RolesGuard) @ApiUseTags("User Groups") @@ -127,4 +127,30 @@ export class UsergroupsAdminController { return group; } + @Roles("admin") + @Get("/:gid/add/:uid") + // region Swagger Docs + @HttpCode(HttpStatus.CREATED) + @ApiOperation({ title: "Add User to Usergroup" }) + @ApiResponse({ + status: HttpStatus.CREATED, description: "Add Success" + }) + // endregion Swagger Docs + public addUser(@Param() param: UserUsergroupDto) { + return this.ugSvr.addUserToGroup(param.gid, param.uid); + } + + @Roles("admin") + @Get("/:gid/remove/:uid") + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Remove User from Usergroup" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Remove Success" + }) + // endregion Swagger Docs + public removeUser(@Param() param: UserUsergroupDto) { + return this.ugSvr.removeUserFromGroup(param.gid, param.uid); + } + } diff --git a/src/modules/usergroups/usergroups.dto.ts b/src/modules/usergroups/usergroups.dto.ts index d87b6e6..fcacae5 100644 --- a/src/modules/usergroups/usergroups.dto.ts +++ b/src/modules/usergroups/usergroups.dto.ts @@ -1,6 +1,7 @@ import { ApiModelProperty } from "@nestjs/swagger"; import { IsString, IsMongoId } from "class-validator"; import { ObjectId } from "@models/common"; +import { IGidDto, IUidDto } from "@dtos/ids"; export class AddUsergroupDto { @ApiModelProperty({ type: String, description: "Usergroup Name" }) @@ -13,3 +14,12 @@ export class EditUsergroupDto { @IsString() public readonly name: string; } + +export class UserUsergroupDto implements IGidDto, IUidDto { + @ApiModelProperty({ type: String, description: "User ID" }) + @IsMongoId() + public readonly uid: ObjectId; + @ApiModelProperty({ type: String, description: "Usergroup ID" }) + @IsMongoId() + public readonly gid: ObjectId; +} diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 8edc326..f1eeabb 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -6,6 +6,7 @@ import { ApiBearerAuth, ApiUseTags, ApiResponse, ApiOperation, ApiImplicitParam } from "@nestjs/swagger"; import { Model as UserModel, IUser, UserDoc } from "@models/User"; +import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { Model as TokensModel } from "@models/Token"; import { Model as GoodsModels } from "@models/Good"; import { CollectionDoc } from "@models/Collection"; @@ -17,12 +18,13 @@ import { TokensService } from "@services/tokens"; import { UsergroupsService } from "@services/usergroups"; import { CollectionsService } from "@services/collections"; import { UsersService } from "@services/users"; +import { SystemService } from "@services/system"; import { PerPageDto, ListResponse, DEF_PER_COUNT } from "@dtos/page"; import { UidDto } from "@dtos/ids"; import { DefResDto } from "@dtos/res"; import { - CreateUserDto, ModifyPasswordDto, EditUserDto, UserUsergroupParamDto + CreateUserDto, ModifyPasswordDto, EditUserDto, UsergroupBodyDto } from "./users.dto"; @UseGuards(RolesGuard) @@ -37,7 +39,8 @@ export class UsersAdminController { private readonly tokensSvr: TokensService, private readonly collectionsSvr: CollectionsService, private readonly usersSvr: UsersService, - private readonly ugSvr: UsergroupsService + private readonly ugSvr: UsergroupsService, + private readonly sysSvr: SystemService ) { } @Roles("admin") @@ -77,14 +80,8 @@ export class UsersAdminController { status: HttpStatus.BAD_REQUEST, description: "Add User Fail" }) // endregion Swagger Docs - public async addUser(@Body() user: CreateUserDto) { - let obj; - try { - obj = await UserModel.addUser(user.username, user.password); - } catch (error) { - throw new BadRequestException(error.toString()); - } - return obj; + public addUser(@Body() user: CreateUserDto) { + return this.usersSvr.addUser(user); } @Roles("admin") @@ -155,12 +152,8 @@ export class UsersAdminController { status: HttpStatus.BAD_REQUEST, description: "Delete User Fail" }) // endregion Swagger Docs - public async delete(@Param() user: UidDto) { - try { - await UserModel.removeUser(user.uid); - } catch (error) { - throw new BadRequestException(error.toString()); - } + public delete(@Param() user: UidDto) { + this.usersSvr.removeUser(user.uid); return new DefResDto(); } @@ -369,26 +362,33 @@ export class UsersAdminController { //////////////////////////////////////// @Roles("admin") - @Get("/:uid/usergroup/:gid") + @Get("/:uid/usergroups") // region Swagger Docs @HttpCode(HttpStatus.OK) - @ApiOperation({ title: "Move Usergroup" }) + @ApiOperation({ title: "Get Usergroup" }) @ApiResponse({ - status: HttpStatus.OK, description: "Move Success", + status: HttpStatus.OK, type: ListResponse }) // endregion Swagger Docs - public async moveUsergroup(@Param() param: UserUsergroupParamDto) { - const group = await this.usersSvr.getUsergroup(param.uid); - if (group) { - const gid = group._id; - if (gid.toString() === param.gid) { - throw new BadRequestException("This is old Usergroup ID"); - } - await this.ugSvr.moveUser(param.gid, param.uid); - } else { - await this.ugSvr.addUserToGroup(param.gid, param.uid); + public async getUsergroups( + @Param() param: UidDto, @Query(new ParseIntPipe()) query: PerPageDto + ) { + const curPage = query.page || 1; + const perNum = query.perNum || DEF_PER_COUNT; + const totalPages = + await this.usersSvr.countPageUsergroups(param.uid, query.perNum); + const totalCount = await this.usersSvr.countUsergroups(param.uid); + + const resData = new ListResponse(); + resData.current = curPage; + resData.totalPages = totalPages; + resData.total = totalCount; + if (totalPages >= curPage) { + resData.data = await this.usersSvr.getUsergroups(param.uid, { + page: curPage, perNum + }); } - return new DefResDto(); + return resData; } //////////////////////////////////////// diff --git a/src/modules/users/users.dto.ts b/src/modules/users/users.dto.ts index e2f2f25..49b6c2d 100644 --- a/src/modules/users/users.dto.ts +++ b/src/modules/users/users.dto.ts @@ -27,11 +27,11 @@ export class ModifyPasswordDto { public readonly newPassword: string; } -export class UserUsergroupParamDto implements IUidDto, IGidDto { - @ApiModelProperty({ type: String, description: "User ID" }) - @IsMongoId() - public readonly uid: ObjectId; +export class UsergroupBodyDto { + @ApiModelProperty({ type: String, description: "Action" }) + @IsString() + public readonly type: "add" | "remove"; @ApiModelProperty({ type: String, description: "Usergroup ID" }) - @IsMongoId() - public readonly gid: ObjectId; + @IsMongoId({ each: true }) + public readonly gids: ObjectId; } diff --git a/test/api/user_usergroup.e2e.ts b/test/api/user_usergroup.e2e.ts index 61128e3..1d611a2 100644 --- a/test/api/user_usergroup.e2e.ts +++ b/test/api/user_usergroup.e2e.ts @@ -3,15 +3,13 @@ import faker = require("faker"); import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { UsersService } from "@services/users"; +import { SystemService } from "@services/system"; +import { UsergroupsService } from "@services/usergroups"; -import { - connect, drop, addCategoryAndRegexp -} from "../helpers/database"; +import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; -import { - newUsergroup, getLinkIdByUserId -} from "../helpers/database/usergroups"; -import { newUser } from "../helpers/database/user"; +import { newUsergroup, getLinkIdsByUserId, getLinkIdsByUsergroupId } from "../helpers/database/usergroups"; +import { newUser, newUserWithUsergroup } from "../helpers/database/user"; describe("User's Usergroup E2E Api", () => { @@ -23,8 +21,7 @@ describe("User's Usergroup E2E Api", () => { const ids = { users: [ ], - usergroups: [ ], - userusergroups: [ ] + usergroups: [ ] }; after(() => { @@ -35,33 +32,51 @@ describe("User's Usergroup E2E Api", () => { request = await init(); }); - let userSvr: UsersService; - before(() => { - userSvr = new UsersService(); - }); - const user = { username: `${faker.name.firstName()}${Math.random()}`, password: faker.random.words() }; step("Login", async () => { - const userDoc = await newUser(user.username, user.password); + const userDoc = await newUserWithUsergroup( + user.username, user.password + ); ids.users.push(userDoc._id); - const groupDoc = await newUsergroup(undefined, userDoc._id); - ids.usergroups.push(groupDoc._id); - ids.userusergroups.push(await getLinkIdByUserId(userDoc._id)); await request.post("/api/v1/auth/login").send(user).then(); }); - step("Move Usergroup", async () => { - const groupDoc = await newUsergroup(); - ids.usergroups.push(groupDoc._id); - const url = `/api/v1/users/${ids.users[0]}/usergroup/${groupDoc._id}`; + step("Get Usergroup", async () => { + const url = `/api/v1/users/${ids.users[0]}/usergroups`; + const { status, body: result } = await request.get(url).then(); + status.should.be.eql(200); + result.should.have.property("total", 1); + }); + + step("User Add 4 Usergroups", async () => { + const uid = ids.users[0]; + for (let i = 0; i < 4; i++) { + const group = await newUsergroup(undefined, uid); + ids.usergroups.push(group._id); + } + }); + + step("Have 5 Usergroups ", async () => { + const url = `/api/v1/users/${ids.users[0]}/usergroups`; + const { status, body: result } = await request.get(url).then(); + status.should.be.eql(200); + result.should.have.property("total", 1 + ids.usergroups.length); + }); + + step("Remove one usergroup", () => { + const uid = ids.users[0]; + const url = `/api/v1/usergroups/${ids.usergroups[0]}/remove/${uid}`; + return request.get(url).then(); + }); + + step("Have 4 Usergroups ", async () => { + const url = `/api/v1/users/${ids.users[0]}/usergroups`; const { status, body: result } = await request.get(url).then(); status.should.be.eql(200); - const group = await userSvr.getUsergroup(ids.users[0]); - group._id.toString().should.be.eql(groupDoc._id.toString()); - group._id.toString().should.be.not.eql(ids.usergroups[0].toString()); + result.should.have.property("total", ids.usergroups.length); }); }); diff --git a/test/api/usergroups.e2e.ts b/test/api/usergroups.e2e.ts index ba57a35..156dc42 100644 --- a/test/api/usergroups.e2e.ts +++ b/test/api/usergroups.e2e.ts @@ -1,15 +1,15 @@ import supertest = require("supertest"); import faker = require("faker"); -import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { UsersService } from "@services/users"; +import { SystemService } from "@services/system"; import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; import { - newUsergroup, getLinkIdByUserId + newUsergroup, getLinkIdsByUserId } from "../helpers/database/usergroups"; import { newUser } from "../helpers/database/user"; @@ -35,11 +35,6 @@ describe("Usergroup E2E Api", () => { request = await init(); }); - let userSvr: UsersService; - before(() => { - userSvr = new UsersService(); - }); - const user = { username: `${faker.name.firstName()}${Math.random()}`, password: faker.random.words() @@ -49,7 +44,7 @@ describe("Usergroup E2E Api", () => { ids.users.push(userDoc._id); const groupDoc = await newUsergroup(undefined, userDoc._id); ids.usergroups.push(groupDoc._id); - ids.userusergroups.push(await getLinkIdByUserId(userDoc._id)); + ids.userusergroups.push(await getLinkIdsByUserId(userDoc._id)); await request.post("/api/v1/auth/login").send(user).then(); }); diff --git a/test/helpers/database.ts b/test/helpers/database.ts index f771f77..3a1cfdf 100644 --- a/test/helpers/database.ts +++ b/test/helpers/database.ts @@ -56,6 +56,11 @@ export const drop = async (ids?: IIds) => { const model = MODEL_IDMETHOD_MAP[method]; for (const id of (ids[method] || [ ])) { await model.findByIdAndRemove(id).exec(); + if (method === "users") { + await UserUsergroupsModel.remove({ user: id }).exec(); + } else if (method === "usergroups") { + await UserUsergroupsModel.remove({ usergroup: id }).exec(); + } } } }; diff --git a/test/helpers/database/user.ts b/test/helpers/database/user.ts index a2d6217..b7fb827 100644 --- a/test/helpers/database/user.ts +++ b/test/helpers/database/user.ts @@ -1,5 +1,7 @@ import { Model as UsersModel } from "@models/User"; import { ObjectId } from "@models/common"; +import { UsersService } from "@services/users"; +import { SystemService } from "@services/system"; import faker = require("faker"); export const newUser = ( @@ -8,3 +10,13 @@ export const newUser = ( ) => { return UsersModel.addUser(username, password); }; + +export const newUserWithUsergroup = ( + username = `${faker.name.firstName()}${Math.random()}`, + password = `${faker.random.words()}${Math.random()}`, + gid?: ObjectId +) => { + return new UsersService(new SystemService()).addUser({ + username, password + }, gid); +}; diff --git a/test/helpers/database/usergroups.ts b/test/helpers/database/usergroups.ts index a61d377..e2401e7 100644 --- a/test/helpers/database/usergroups.ts +++ b/test/helpers/database/usergroups.ts @@ -5,14 +5,17 @@ import faker = require("faker"); /// -export const getLinkIdByUserId = async (uid: ObjectId) => { - return (await UserUsergroupsModel.findOne({ user: uid }).exec())._id; +export const getLinkIdsByUserId = async (uid: ObjectId) => { + return (await UserUsergroupsModel.find({ user: uid }).exec()) + .map((item) => { + return item._id as ObjectId; + }); }; export const getLinkIdsByUsergroupId = async (gid: ObjectId) => { return (await UserUsergroupsModel.find({ usergroup: gid }).exec()) .map((item) => { - return item._id; + return item._id as ObjectId; }); }; diff --git a/test/issues/ban_user_n_its_token.e2e.ts b/test/issues/ban_user_n_its_token.e2e.ts index 98ea6ee..5d3e5e9 100644 --- a/test/issues/ban_user_n_its_token.e2e.ts +++ b/test/issues/ban_user_n_its_token.e2e.ts @@ -5,12 +5,13 @@ import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; import { UsersService } from "@services/users"; import { TokensService } from "@services/tokens"; +import { SystemService } from "@services/system"; describe("Fix Issues", () => { let request: supertest.SuperTest; const tokensSvr = new TokensService(); - const usersSvr = new UsersService(); + const usersSvr = new UsersService(new SystemService()); before(() => { return connect(); diff --git a/test/services/users.spec.ts b/test/services/users.spec.ts index bc0fcaa..56df9ab 100644 --- a/test/services/users.spec.ts +++ b/test/services/users.spec.ts @@ -1,4 +1,5 @@ import { UsersService } from "@services/users"; +import { SystemService } from "@services/system"; import { Model as UsersModel } from "@models/User"; import db = require("../helpers/database"); import faker = require("faker"); @@ -20,7 +21,7 @@ describe("Users Service Test Unit", () => { }); beforeEach(() => { - usersSvr = new UsersService(); + usersSvr = new UsersService(new SystemService()); }); const user = { From 2de7000f87b37008e1ecc81ee02260b5f84b6a05 Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 5 Mar 2018 18:14:08 +0800 Subject: [PATCH 12/49] Add user into usergroup validator --- .../common/services/usergroups.service.ts | 18 +++++++--- test/api/usergroups.e2e.ts | 35 ++++++++++++++----- test/helpers/database/usergroups.ts | 11 +++--- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index 78c20e5..98f5cdb 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -1,4 +1,5 @@ import { Component, BadRequestException } from "@nestjs/common"; +import { Model as UsersModel } from "@models/User"; import { Model as UsergroupsModel } from "@models/Usergroup"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { ObjectId } from "@models/common"; @@ -86,10 +87,19 @@ export class UsergroupsService { } public async addUserToGroup(gid: ObjectId, uid: ObjectId) { - // TODO User Check - // TODO Usergroup Check + if (!(await UsersModel.findById(uid).exec())) { + throw new BadRequestException("The User ID is not exist"); + } + if (!(await UsergroupsModel.findById(gid).exec())) { + throw new BadRequestException("The Usergroup ID is not exist"); + } + if (await UserUsergroupsModel.findOne({ + user: uid, usergroup: gid + }).exec()) { + throw new BadRequestException("User has been in the usergroup"); + } try { - await UserUsergroupsModel.create({ + return await UserUsergroupsModel.create({ user: uid, usergroup: gid }); } catch (error) { @@ -98,8 +108,6 @@ export class UsergroupsService { } public async removeUserFromGroup(gid: ObjectId, uid: ObjectId) { - // TODO User Check - // TODO Usergroup Check try { await UserUsergroupsModel.findOneAndRemove({ user: uid, usergroup: gid diff --git a/test/api/usergroups.e2e.ts b/test/api/usergroups.e2e.ts index 156dc42..057b57d 100644 --- a/test/api/usergroups.e2e.ts +++ b/test/api/usergroups.e2e.ts @@ -1,9 +1,6 @@ import supertest = require("supertest"); import faker = require("faker"); -import { UsersService } from "@services/users"; -import { SystemService } from "@services/system"; - import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; @@ -11,7 +8,7 @@ import { init } from "../helpers/server"; import { newUsergroup, getLinkIdsByUserId } from "../helpers/database/usergroups"; -import { newUser } from "../helpers/database/user"; +import { newUser, newUserWithUsergroup } from "../helpers/database/user"; describe("Usergroup E2E Api", () => { @@ -40,11 +37,9 @@ describe("Usergroup E2E Api", () => { password: faker.random.words() }; step("Login", async () => { - const userDoc = await newUser(user.username, user.password); + const userDoc = + await newUserWithUsergroup(user.username, user.password); ids.users.push(userDoc._id); - const groupDoc = await newUsergroup(undefined, userDoc._id); - ids.usergroups.push(groupDoc._id); - ids.userusergroups.push(await getLinkIdsByUserId(userDoc._id)); await request.post("/api/v1/auth/login").send(user).then(); }); @@ -76,6 +71,30 @@ describe("Usergroup E2E Api", () => { result.users.total.should.be.eql(0); }); + step("Add User into Usergroup", async () => { + const gid = ids.usergroups[ids.usergroups.length - 1]; + const uid = ids.users[0]; + const url = `/api/v1/usergroups/${gid}/add/${uid}`; + const { status } = await request.get(url).then(); + status.should.be.eql(201); + }); + + step("Have 1 User", async () => { + const id = ids.usergroups[ids.usergroups.length - 1]; + const url = `/api/v1/usergroups/${id}`; + const { status, body: result } = await request.get(url).then(); + status.should.be.eql(200); + result.users.total.should.be.eql(1); + }); + + step("Fail to Add User into Same Usergroup", async () => { + const gid = ids.usergroups[ids.usergroups.length - 1]; + const uid = ids.users[0]; + const url = `/api/v1/usergroups/${gid}/add/${uid}`; + const { status } = await request.get(url).then(); + status.should.be.eql(400); + }); + step("Modify Usergroup's name", async () => { const id = ids.usergroups[ids.usergroups.length - 1]; const url = `/api/v1/usergroups/${id}`; diff --git a/test/helpers/database/usergroups.ts b/test/helpers/database/usergroups.ts index e2401e7..2e5a39f 100644 --- a/test/helpers/database/usergroups.ts +++ b/test/helpers/database/usergroups.ts @@ -1,7 +1,7 @@ +import faker = require("faker"); import { ObjectId } from "@models/common"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; -import { Model as UsergroupsModel } from "@models/Usergroup"; -import faker = require("faker"); +import { UsergroupsService } from "@services/usergroups"; /// @@ -22,11 +22,10 @@ export const getLinkIdsByUsergroupId = async (gid: ObjectId) => { export const newUsergroup = async ( name = `${faker.random.word}${Math.random()}`, uid?: ObjectId ) => { - const group = await UsergroupsModel.create({ name }); + const svr = new UsergroupsService(); + const group = await svr.add({ name }); if (uid) { - const link = await UserUsergroupsModel.create({ - user: uid, usergroup: group._id - }); + await svr.addUserToGroup(group._id, uid); } return group; }; From 6e9214bdebbd6830a46f1bd4195ccab2c6ebc1e0 Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 7 Mar 2018 11:49:46 +0800 Subject: [PATCH 13/49] Support for default values in config file --- config/app.default.yaml | 8 +++++++- src/modules/database/database.providers.ts | 8 ++++++-- src/utils/config.d.ts | 15 ++++++++++++++ src/utils/config.ts | 23 +++++++++++++++++++++- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/config/app.default.yaml b/config/app.default.yaml index 8ff3ff5..d94193e 100644 --- a/config/app.default.yaml +++ b/config/app.default.yaml @@ -14,4 +14,10 @@ paths: upload: upload log: log resource: resource - backup: backup \ No newline at end of file + backup: backup +defaults: + user: + name: root + pass: admin + group: + name: admin \ No newline at end of file diff --git a/src/modules/database/database.providers.ts b/src/modules/database/database.providers.ts index eff2f04..5b40776 100644 --- a/src/modules/database/database.providers.ts +++ b/src/modules/database/database.providers.ts @@ -39,11 +39,15 @@ export const connectDatabase = () => { export const injectData = async () => { let num = await UsersModel.count({ }).exec(); if (num === 0) { - return UsersModel.addUser("root", "admin"); + return UsersModel.addUser( + config.defaults.user.name, config.defaults.user.pass + ); } num = await UsergroupsModel.count({ }).exec(); if (num === 0) { - const group = await UsergroupsModel.create({ name: "admin" }); + const group = await UsergroupsModel.create({ + name: config.defaults.group.name + }); const conditions = (await UsersModel.find({ }).exec()) .map((item) => { return { diff --git a/src/utils/config.d.ts b/src/utils/config.d.ts index 2556f66..6f3df17 100644 --- a/src/utils/config.d.ts +++ b/src/utils/config.d.ts @@ -23,9 +23,24 @@ interface redis { url: string; } +interface IDefaults { + user: IDefaultUser; + group: IDefaultUsergroup; +} + +interface IDefaultUser { + name: string; + pass: string; +} + +interface IDefaultUsergroup { + name: string; +} + export interface ConfigObj { redis: redis; server: server; db: db; paths: paths; + defaults: IDefaults; } diff --git a/src/utils/config.ts b/src/utils/config.ts index 5f8f5b3..aea0453 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -18,21 +18,42 @@ for (const filepath of CONFIG_FILEPATHS) { } } +// region Paths Process for (const item of Object.keys(configModule.paths)) { const curPath = configModule.paths[item]; if (!ph.isAbsolute(configModule.paths[item])) { configModule.paths[item] = ph.resolve(`${__dirname}/../../${curPath}`); } } +// endregion Paths Process +// region Redis Process configModule.redis.url = configModule.redis.url || - `redis://${configModule.redis.host}:${configModule.redis.port}`; +`redis://${configModule.redis.host}:${configModule.redis.port}`; if ( configModule.redis.url.indexOf("redis://") !== 0 && configModule.redis.url.indexOf("//") !== 0 ) { configModule.redis.url = `redis://${configModule.redis.url}`; } +// endregion Redis Process + +// region Default Values Check +try { + const values = [ + configModule.defaults.user.name, + configModule.defaults.user.pass, + configModule.defaults.group.name + ]; + for (const item of values) { + if (item.length < 4) { + throw new TypeError("Default Value Must great than 4 words."); + } + } +} catch (error) { + throw new TypeError(error.toString()); +} +// endregion Default Values Check export const config = configModule.getConfig() as ConfigObj; From 86fd1abff6419d867c16c750f33053b2d23cd0bb Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 8 Mar 2018 10:48:49 +0800 Subject: [PATCH 14/49] Update Add - Test Unit - ToArray Pipe - categories Service - support for upload file with query Update - move regexp's some schema's methods to its service - the function of generate categories on test unit - the function of login factory on test unit - the function of generate file factory on test unit - the function of faker new name on test unit - support for test url with query on test unit Fix - lock nestjs's version on 4.5.2 - lost `description` --- package.json | 6 +- src/index.ts | 2 +- src/models/Categroy.ts | 2 +- src/models/Regexp.ts | 102 +++-------------- .../common/pipes/regexp-count-check.pipe.ts | 7 +- src/modules/common/pipes/to-array.pipe.ts | 34 ++++++ .../common/services/categories.service.ts | 58 ++++++++++ .../common/services/regexps.service.ts | 33 ++++-- src/modules/controllers.module.ts | 3 +- src/modules/goods/goods.controller.ts | 104 ++++++++++++----- src/modules/goods/goods.dto.ts | 17 ++- src/modules/regexps/regexps.controller.ts | 3 + src/modules/regexps/regexps.dto.ts | 5 + src/modules/users/users.controller.ts | 2 +- test/api/categroies.e2e.ts | 14 +-- test/api/collections_token.e2e.ts | 12 +- test/api/goods.e2e.ts | 16 +-- test/api/goods_specified_category.e2e.ts | 105 ++++++++++++++++++ test/api/regexps.e2e.ts | 46 ++++---- test/helpers/database.ts | 25 +++-- test/helpers/database/auth.ts | 16 +++ test/helpers/database/categories.ts | 52 +++++++++ test/helpers/database/goods.ts | 5 + test/helpers/database/regexps.ts | 16 +++ test/helpers/database/usergroups.ts | 5 +- test/helpers/files.ts | 77 ++++++++++++- test/helpers/utils.ts | 8 ++ test/models/Categroies.spec.ts | 21 +--- test/models/regexp.spec.ts | 70 ++++++------ 29 files changed, 611 insertions(+), 255 deletions(-) create mode 100644 src/modules/common/pipes/to-array.pipe.ts create mode 100644 src/modules/common/services/categories.service.ts create mode 100644 test/api/goods_specified_category.e2e.ts create mode 100644 test/helpers/database/auth.ts create mode 100644 test/helpers/database/categories.ts create mode 100644 test/helpers/database/goods.ts create mode 100644 test/helpers/database/regexps.ts diff --git a/package.json b/package.json index 70e0fb3..7a5edd7 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "author": "AryloYeung ", "license": "MIT", "devDependencies": { - "@nestjs/testing": "^4.5.1", + "@nestjs/testing": "4.5.2", "@types/basic-auth": "^1.1.2", "@types/bunyan": "^1.8.4", "@types/connect-redis": "0.0.7", @@ -67,8 +67,8 @@ "typescript": "^2.6.1" }, "dependencies": { - "@nestjs/common": "^4.5.1", - "@nestjs/core": "^4.5.1", + "@nestjs/common": "4.5.2", + "@nestjs/core": "4.5.2", "@nestjs/swagger": "^1.1.3", "basic-auth": "^2.0.0", "body-parser": "^1.18.2", diff --git a/src/index.ts b/src/index.ts index 8b085f1..54dac33 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ import { isDevelopment } from "./modules/common/helper/env"; const bootstrap = async () => { const server = initExpress(); - const app = await NestFactory.create(ApplicationModule, server, { }); + const app = await NestFactory.create(ApplicationModule, server); app.useGlobalPipes(new ValidationPipe()); if (isDevelopment) { diff --git a/src/models/Categroy.ts b/src/models/Categroy.ts index aeef6b4..bf46d10 100644 --- a/src/models/Categroy.ts +++ b/src/models/Categroy.ts @@ -7,7 +7,7 @@ import { reduce, includes, difference } from "lodash"; import { MongoError } from "mongodb"; import Cache = require("schedule-cache"); -const cache = Cache.create(`${Date.now()}${Math.random()}`); +export const cache = Cache.create(`${Date.now()}${Math.random()}`); export const FLAG = "categories"; export type CategoryDoc = IDoc; diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index 820df80..dbcb26f 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -30,20 +30,18 @@ export interface IRegexpsRaw extends IRegexp { link: ICategory; } +export interface IRegexpDoc { + name: string; + value: string; + link?: ObjectId; + hidden?: boolean; +} + export type RegexpDoc = IDoc; const RegexpSchema = new Base(Definition).createSchema(); // region static methods -RegexpSchema.static("countRegexps", async (perNum = 1) => { - const FLAG = `page_count_${perNum}`; - if (cache.get(FLAG)) { - return cache.get(FLAG); - } - cache.put(FLAG, Math.ceil((await Model.count({ }).exec()) / perNum)); - return cache.get(FLAG); -}); - RegexpSchema.static( "addRegexp", (name: string, value: string, link?: ObjectId) => { @@ -62,55 +60,6 @@ RegexpSchema.static("removeRegexp", (id: ObjectId) => { return Model.findByIdAndRemove(id).exec(); }); -RegexpSchema.static("link", (id: ObjectId, linkId: ObjectId | false) => { - if (!linkId) { - return Model.findByIdAndUpdate(id, { - "$unset": { link: 0 } - }).exec(); - } else { - return Model.findByIdAndUpdate( - id, { link: linkId }, { runValidators: true } - ).exec(); - } -}); - -RegexpSchema.static("list", (perNum = DEF_PER_COUNT, page = 1) => { - const FLAG_LIST = `list_${perNum}_${page}`; - if (cache.get(FLAG_LIST)) { - return cache.get(FLAG_LIST); - } - cache.put( - FLAG_LIST, - Model.find({ }) - .skip((page - 1) * perNum).limit(perNum) - .populate("link").exec() - ); - return cache.get(FLAG_LIST); -}); - -RegexpSchema.static("discern", (name: string) => { - const FLAG_DISCER_LIST = "discern"; - let p: Promise; - if (cache.get(FLAG_DISCER_LIST)) { - p = cache.get(FLAG_DISCER_LIST); - } else { - p = Model.find({ link: { $exists: true }, hidden: false }) - .populate("link") - .exec(); - cache.put(FLAG_DISCER_LIST, p); - } - return p.then((result) => { - const list = [ ]; - result.forEach((item) => { - const obj = item.toObject(); - const reg = new RegExp(obj.value); - if (reg.test(name)) { - list.push(obj.link); - } - }); - return list; - }); -}); // endregion static methods export const FLAG = "regexps"; @@ -126,27 +75,6 @@ interface IRegexpModel extends M { * @return {Promise} */ removeRegexp(id: ObjectId): Promise; - /** - * 规则关联 - * @return {Promise} - */ - link(id: ObjectId, linkId: ObjectId | false): Promise; - /** - * 规则列表 - * @param perNum {number} 每页数量 - * @param page {number} 页数 - * @return {Promise} - */ - list(perNum?: number, page?: number): Promise; - /** - * 根据规则进行识别 - * @return {Promise} - */ - discern(filename: string): Promise; - /** - * 返回总页数 - */ - countRegexps(perNum?: number): Promise; } // region Validators @@ -154,7 +82,7 @@ RegexpSchema.path("name").validate({ isAsync: true, validator: async (value, respond) => { const result = await Model.findOne({ name: value }).exec(); - return !result; + return respond(!result); }, message: "The name is exist" }); @@ -162,16 +90,20 @@ RegexpSchema.path("name").validate({ RegexpSchema.path("value").validate({ isAsync: true, validator: (value, respond) => { - return isRegExp(value); + return respond(isRegExp(value)); }, message: "The value isnt Regexp" }); RegexpSchema.path("value").validate({ isAsync: true, - validator: async (value, respond) => { - const result = await Model.findOne({ value: value }).exec(); - return !result; + validator: async function ValueExistValdator(value, respond) { + if (this && this.hidden) { + return respond(true); + } + const result = + await Model.findOne({ value: value, hidden: false }).exec(); + return respond(!result); }, message: "The value is exist" }); @@ -180,7 +112,7 @@ RegexpSchema.path("link").validate({ isAsync: true, validator: async (value, respond) => { const result = await CM.findById(value).exec(); - return !!result; + return respond(!!result); }, message: "The Category ID is not exist" }); diff --git a/src/modules/common/pipes/regexp-count-check.pipe.ts b/src/modules/common/pipes/regexp-count-check.pipe.ts index dfe2a03..785e002 100644 --- a/src/modules/common/pipes/regexp-count-check.pipe.ts +++ b/src/modules/common/pipes/regexp-count-check.pipe.ts @@ -1,16 +1,19 @@ import { Pipe, PipeTransform, ArgumentMetadata, BadRequestException } from "@nestjs/common"; -import { Model as RegexpModel } from "@models/Regexp"; import { isArray } from "util"; import fs = require("fs-extra"); +import { RegexpsService } from "@services/regexps"; type File = Express.Multer.File; @Pipe() export class RegexpCountCheckPipe implements PipeTransform { + + private readonly regexpSvr = new RegexpsService(); + public async transform(value: File | File[], metadata: ArgumentMetadata) { - const regexpCount = (await RegexpModel.list()).length; + const regexpCount = await this.regexpSvr.count(); if (regexpCount !== 0) { return value; } diff --git a/src/modules/common/pipes/to-array.pipe.ts b/src/modules/common/pipes/to-array.pipe.ts new file mode 100644 index 0000000..776ccbd --- /dev/null +++ b/src/modules/common/pipes/to-array.pipe.ts @@ -0,0 +1,34 @@ +import { Pipe, PipeTransform, ArgumentMetadata } from "@nestjs/common"; +import { isObject, isArray } from "util"; + +@Pipe() +export class ToArrayPipe implements PipeTransform { + + private readonly properties: string[]; + constructor(...properties: string[]) { + this.properties = properties; + } + + public async transform(value: any, metadata: ArgumentMetadata) { + if (!value || isArray(value)) { + return value; + } + if (this.properties.length === 0) { + if (isObject(value)) { + for (const key of Object.keys(value)) { + value[key] = [ value[key] ]; + } + } else { + return [ value ]; + } + } else { + for (const property of this.properties) { + if (!value[property] || isArray(value[property])) { + continue; + } + value[property] = [ value[property] ]; + } + } + return value; + } +} diff --git a/src/modules/common/services/categories.service.ts b/src/modules/common/services/categories.service.ts new file mode 100644 index 0000000..e071783 --- /dev/null +++ b/src/modules/common/services/categories.service.ts @@ -0,0 +1,58 @@ +import { Component } from "@nestjs/common"; +import { ObjectId } from "@models/common"; +import { Model as CategoriesModel, cache } from "@models/Categroy"; +import { isFunction } from "util"; + +interface IIdMap { + [parentId: string]: ObjectId[]; +} + +@Component() +export class CategoriesService { + + private loadAndCache(FLAG: string, value: () => any, time?: number) { + const c = cache.get(FLAG); + if (c) { + return c; + } + const val = value(); + cache.put(FLAG, val, time); + return val; + } + + private async getIdMap() { + // { parentId: childrenIds } + const map: IIdMap = { }; + const docs = await CategoriesModel.find().select("_id pid").exec(); + docs.forEach((doc) => { + const category = doc.toObject(); + let index; + if (!category.pid) { + index = "*"; + } else { + index = category.pid.toString(); + } + if (!map[index]) { + map[index] = [ ]; + } + map[index].push(category._id.toString()); + }); + return map; + } + + public async getChildrenIds(pid: ObjectId) { + const map: IIdMap = await this.loadAndCache("IdMap", () => { + return this.getIdMap(); + }); + const ids: ObjectId[] = [ ]; + const childrenIds = map[pid.toString()]; + if (childrenIds) { + ids.push(...childrenIds); + for (const id of childrenIds) { + ids.push(...(await this.getChildrenIds(id))); + } + } + return ids; + } + +} diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index cb9c23d..b742f28 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -1,11 +1,13 @@ import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; -import { Model as RegexpsModel, cache, RegexpDoc } from "@models/Regexp"; +import { + Model as RegexpsModel, cache, RegexpDoc, IRegexpDoc +} from "@models/Regexp"; import { Model as CategroiesModel, ICategory } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; import { isUndefined } from "util"; -interface IGetRegexpsOptions { +export interface IGetRegexpsOptions { categroies?: ObjectId[]; appends?: ObjectId[]; } @@ -13,6 +15,17 @@ interface IGetRegexpsOptions { @Component() export class RegexpsService { + constructor() { + // Update + setTimeout(() => { + // Add Hidden Label + RegexpsModel.update( + { hidden: { $exists: false } }, { hidden: false }, + { multi: true } + ).exec(); + }, 3000); + } + private loadAndCache(FLAG: string, value: any, time?: number | string) { if (cache.get(FLAG) === null) { cache.put(FLAG, value, time); @@ -23,7 +36,7 @@ export class RegexpsService { /** * 新增规则 */ - public async create(obj: object) { + public async create(obj: IRegexpDoc) { try { return await RegexpsModel.create(obj); } catch (error) { @@ -37,7 +50,9 @@ export class RegexpsService { public async editById(id: ObjectId, obj) { try { return await RegexpsModel - .findByIdAndUpdate(id, obj, { runValidators: true }) + .update( + { _id: id }, obj, { runValidators: true, context: "query" } + ) .exec(); } catch (error) { throw new BadRequestException(error.toString()); @@ -122,8 +137,12 @@ export class RegexpsService { ); } - private async getRegexps(opts: IGetRegexpsOptions): Promise { - const DEF_CONDITIONS = { link: { $exists: true }, hidden: false }; + private async getRegexps(opts: IGetRegexpsOptions = { }) + : Promise { + const DEF_CONDITIONS = { + link: { $exists: true }, hidden: false + }; + if (opts.categroies && opts.categroies.length > 0) { // 指定Categroy const FLAG = `categroies_scan_regexps_${opts.categroies.join("_")}`; @@ -166,7 +185,7 @@ export class RegexpsService { * 根据规则进行识别 * @return {Promise} */ - public async discern(name: string, opts: IGetRegexpsOptions) { + public async discern(name: string, opts?: IGetRegexpsOptions) { const result = await this.getRegexps(opts); const list = [ ]; result.forEach((item) => { diff --git a/src/modules/controllers.module.ts b/src/modules/controllers.module.ts index 7b506e8..5914375 100644 --- a/src/modules/controllers.module.ts +++ b/src/modules/controllers.module.ts @@ -37,6 +37,7 @@ import { UsersService } from "@services/users"; import { TokensService } from "@services/tokens"; import { UsergroupsService } from "@services/usergroups"; import { SystemService } from "@services/system"; +import { CategoriesService } from "@services/categories"; // endregion Services export const controllers = [ @@ -50,7 +51,7 @@ export const controllers = [ ]; export const services = [ - RegexpsService, + RegexpsService, CategoriesService, CollectionsService, TokensService, UsersService, UsergroupsService, SystemService ]; diff --git a/src/modules/goods/goods.controller.ts b/src/modules/goods/goods.controller.ts index f72dc0b..fc6779f 100644 --- a/src/modules/goods/goods.controller.ts +++ b/src/modules/goods/goods.controller.ts @@ -11,6 +11,7 @@ import { Model as GoodsModels, IGoods } from "@models/Good"; import { Model as RegexpModel } from "@models/Regexp"; import { Model as TokensModel } from "@models/Token"; import { Model as CollectionsModel } from "@models/Collection"; +import { Model as CategroiesModel } from "@models/Categroy"; import { ObjectId } from "@models/common"; import { config } from "@utils/config"; import { RolesGuard } from "@guards/roles"; @@ -21,15 +22,18 @@ import { IReqUser } from "@dtos/req"; import { PerPageDto, ListResponse } from "@dtos/page"; import { RegexpCountCheckPipe } from "@pipes/regexp-count-check"; import { ParseIntPipe } from "@pipes/parse-int"; +import { ToArrayPipe } from "@pipes/to-array"; import { TokensService } from "@services/tokens"; import { CollectionsService } from "@services/collections"; +import { IGetRegexpsOptions, RegexpsService } from "@services/regexps"; +import { CategoriesService } from "@services/categories"; import * as hasha from "hasha"; import fs = require("fs-extra"); import multer = require("multer"); import { isArray } from "util"; import { CreateValueDto, EditValueDto } from "../values/values.dto"; -import { GoodAttributeParamDto } from "./goods.dto"; +import { GoodAttributeParamDto, UploadQueryDto } from "./goods.dto"; @UseGuards(RolesGuard) @Controller("api/v1/goods") @@ -40,7 +44,9 @@ export class GoodsAdminController { constructor( private readonly tokensSvr: TokensService, - private readonly collectionsSvr: CollectionsService + private readonly collectionsSvr: CollectionsService, + private readonly regexpSvr: RegexpsService, + private readonly categoriesSvr: CategoriesService ) { } private toMd5sum(filepath: string) { @@ -79,13 +85,44 @@ export class GoodsAdminController { return resData; } + private async getCategoriesIds(names: string[]) { + if (names.length === 0) { + return [ ]; + } + const conditions = { + $or: names.reduce((arr: any[], item) => { + arr.push({ name: item }); + return arr; + }, [ ]) + }; + const categories = await CategroiesModel.find(conditions).exec(); + const idSet = new Set(); + for (const category of categories) { + const id = category._id.toString(); + idSet.add(id); + const ids = await this.categoriesSvr.getChildrenIds(id); + for (const id of ids) { + idSet.add(id.toString()); + } + } + return Array.from(idSet); + } + + /** + * 文件处理 + */ private async fileProcess( - file: Express.Multer.File, uploader: string, + obj: { + file: Express.Multer.File, uploader: string, + opt?: IGetRegexpsOptions + }, cb?: (type: "Categories" | "Good", error) => void ) { - const categories = await RegexpModel.discern(file.originalname); + const categories = await this.regexpSvr.discern( + obj.file.originalname, obj.opt + ); if (categories.length !== 1) { - fs.remove(file.path); + fs.remove(obj.file.path); if (cb) { cb("Categories", categories.length); } @@ -93,13 +130,13 @@ export class GoodsAdminController { } let goodObj: IGoods; try { - const md5sum = this.toMd5sum(file.path); - const sha256sum = this.toSha256sum(file.path); + const md5sum = this.toMd5sum(obj.file.path); + const sha256sum = this.toSha256sum(obj.file.path); goodObj = (await GoodsModels.create({ - filename: file.filename, - originname: file.originalname, + filename: obj.file.filename, + originname: obj.file.originalname, category: categories[0]._id, - uploader, md5sum, sha256sum, + uploader: obj.uploader, md5sum, sha256sum, active: true })).toObject(); } catch (error) { @@ -109,8 +146,8 @@ export class GoodsAdminController { return; } const newFilePath = - `${config.paths.upload}/${categories[0]._id}/${file.filename}`; - fs.move(file.path, newFilePath); + `${config.paths.upload}/${categories[0]._id}/${obj.file.filename}`; + fs.move(obj.file.path, newFilePath); return goodObj; } @@ -126,23 +163,32 @@ export class GoodsAdminController { // endregion Swagger Docs public async addGood( @File(new RegexpCountCheckPipe()) file: Express.Multer.File, - @User() user: IReqUser, @Session() session + @User() user: IReqUser, @Session() session, + @Query(new ToArrayPipe()) query: UploadQueryDto ) { const uploaderId = session.loginUserId || await this.tokensSvr.getIdByToken(user.token); + const fileProcessOpts = { + categroies: + await this.getCategoriesIds(query.category || []), + appends: await this.getCategoriesIds(query.append || []) + }; - return await this.fileProcess(file, uploaderId, (type, error) => { - if (type === "Categories") { - if (error === 0) { - throw new BadRequestException("Lost Role for the file"); - } else { - throw new BadRequestException("Much Role for the file"); + return await this.fileProcess( + { file, uploader: uploaderId, opt: fileProcessOpts }, + (type, error) => { + if (type === "Categories") { + if (error === 0) { + throw new BadRequestException("Lost Role for the file"); + } else { + throw new BadRequestException("Much Role for the file"); + } + } + if (type === "Good") { + throw new BadRequestException(error.toString()); } } - if (type === "Good") { - throw new BadRequestException(error.toString()); - } - }); + ); } @Roles("admin", "token") @@ -157,14 +203,22 @@ export class GoodsAdminController { // endregion Swagger Docs public async addGoods( @Files(new RegexpCountCheckPipe()) files: Express.Multer.File[], - @User() user: IReqUser, @Session() session + @User() user: IReqUser, @Session() session, + @Query(new ToArrayPipe()) query: UploadQueryDto ) { const uploaderId = session.loginUserId || await this.tokensSvr.getIdByToken(user.token); + const fileProcessOpts = { + categroies: + await this.getCategoriesIds(query.category || []), + appends: await this.getCategoriesIds(query.append || []) + }; const goods: IGoods[] = [ ]; for (const file of files) { - const goodObj = await this.fileProcess(file, uploaderId); + const goodObj = await this.fileProcess({ + file, uploader: uploaderId, opt: fileProcessOpts + }); if (!goodObj) { fs.remove(file.path); continue; diff --git a/src/modules/goods/goods.dto.ts b/src/modules/goods/goods.dto.ts index a6ea7b1..84b9897 100644 --- a/src/modules/goods/goods.dto.ts +++ b/src/modules/goods/goods.dto.ts @@ -1,6 +1,6 @@ import { IGidDto, IAidDto } from "@dtos/ids"; import { ApiModelProperty } from "@nestjs/swagger"; -import { IsMongoId } from "class-validator"; +import { IsMongoId, IsString, IsOptional } from "class-validator"; import { ObjectId } from "@models/common"; import { IGoods } from "@models/Good"; @@ -19,3 +19,18 @@ export class GoodsDto { @ApiModelProperty({ type: Object, description: "Goods", isArray: true }) public readonly goods: IGoods[]; } + +export class UploadQueryDto { + @ApiModelProperty({ + type: String, description: "Category Name", isArray: true + }) + @IsString({ each: true }) + @IsOptional() + public readonly category?: string[]; + @ApiModelProperty({ + type: String, description: "Append Category Name", isArray: true + }) + @IsString({ each: true }) + @IsOptional() + public readonly append?: string[]; +} diff --git a/src/modules/regexps/regexps.controller.ts b/src/modules/regexps/regexps.controller.ts index f68353a..4eda6ef 100644 --- a/src/modules/regexps/regexps.controller.ts +++ b/src/modules/regexps/regexps.controller.ts @@ -85,6 +85,9 @@ export class RegexpsAdminController { if (ctx.name) { data.name = ctx.name; } if (ctx.value) { data.value = ctx.value; } if (ctx.link) { data.link = ctx.link; } + if (ctx.hidden !== null && ctx.hidden !== undefined) { + data.hidden = ctx.hidden; + } if (Object.keys(data).length === 0) { throw new BadRequestException("No Params"); } diff --git a/src/modules/regexps/regexps.dto.ts b/src/modules/regexps/regexps.dto.ts index 6078aaa..43fb6bf 100644 --- a/src/modules/regexps/regexps.dto.ts +++ b/src/modules/regexps/regexps.dto.ts @@ -17,6 +17,7 @@ export class EditRegexpRawDot implements IRegexp { public name?: string; public value?: string; public link?: ObjectId; + public hidden?: boolean; } export class NewRegexp implements INewRegexp { @@ -45,4 +46,8 @@ export class EditRegexpDot implements IRegexp { @IsOptional() @IsMongoId() public readonly link: ObjectId; + @ApiModelPropertyOptional({ type: Boolean }) + @IsOptional() + @IsMongoId() + public readonly hidden: boolean; } diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index f1eeabb..2f61db4 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -367,7 +367,7 @@ export class UsersAdminController { @HttpCode(HttpStatus.OK) @ApiOperation({ title: "Get Usergroup" }) @ApiResponse({ - status: HttpStatus.OK, type: ListResponse + status: HttpStatus.OK, description: "Usergroup List", type: ListResponse }) // endregion Swagger Docs public async getUsergroups( diff --git a/test/api/categroies.e2e.ts b/test/api/categroies.e2e.ts index c1d0f21..54b24cf 100644 --- a/test/api/categroies.e2e.ts +++ b/test/api/categroies.e2e.ts @@ -3,6 +3,7 @@ import faker = require("faker"); import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; +import { login } from "../helpers/database/auth"; describe("Categories E2E Api", () => { @@ -26,17 +27,8 @@ describe("Categories E2E Api", () => { request = await init(); }); - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + step("login", async () => { + ids.users.push((await login(request))[0]); }); step("Add Category", async () => { diff --git a/test/api/collections_token.e2e.ts b/test/api/collections_token.e2e.ts index c3ac298..14a3f73 100644 --- a/test/api/collections_token.e2e.ts +++ b/test/api/collections_token.e2e.ts @@ -10,7 +10,7 @@ import { connect, drop, newUser, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; -import { uploadFiles } from "../helpers/files"; +import { uploadFiles, newFile } from "../helpers/files"; describe("Token to Upload Files Api", () => { @@ -40,17 +40,9 @@ describe("Token to Upload Files Api", () => { const filepaths = [ ]; const prefix = `${faker.random.word()}_`; before(() => { - const folderpath = `${config.paths.tmp}/test`; - if (!fs.existsSync(folderpath)) { - fs.mkdirpSync(folderpath); - } // Generator Files for (let i = 0; i < FILE_COUNST; i++) { - const filepath = `${folderpath}/${prefix}${faker.random.uuid()}`; - filepaths.push(filepath); - fs.writeFileSync(filepath, JSON.stringify({ - data: Math.random() - }), { encoding: "utf-8" }); + filepaths.push(newFile(`${prefix}${faker.random.uuid()}`)); } }); diff --git a/test/api/goods.e2e.ts b/test/api/goods.e2e.ts index d6dc327..9932fdc 100644 --- a/test/api/goods.e2e.ts +++ b/test/api/goods.e2e.ts @@ -2,17 +2,18 @@ import supertest = require("supertest"); import path = require("path"); import faker = require("faker"); -import { connect, drop, addCategoryAndRegexp, newUser } from "../helpers/database"; +import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { uploadFile } from "../helpers/files"; import { init } from "../helpers/server"; +import auth = require("../helpers/database/auth"); +import { newName } from "../helpers/utils"; describe("Goods E2E Api", () => { let request: supertest.SuperTest; const user = { - name: faker.name.firstName(), - pass: faker.random.words() + name: newName() }; before(async () => { @@ -35,13 +36,8 @@ describe("Goods E2E Api", () => { request = await init(); }); - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - const { body: result } = await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push((await auth.login(request, user.name))[0]); }); step("Add Category", async () => { diff --git a/test/api/goods_specified_category.e2e.ts b/test/api/goods_specified_category.e2e.ts new file mode 100644 index 0000000..501aa7c --- /dev/null +++ b/test/api/goods_specified_category.e2e.ts @@ -0,0 +1,105 @@ +import * as supertest from "supertest"; +import db = require("../helpers/database"); +import path = require("path"); +import files = require("../helpers/files"); +import auth = require("../helpers/database/auth"); +import categories = require("../helpers/database/categories"); +import { init } from "../helpers/server"; +import { newName } from "../helpers/utils"; +import * as regexps from "../helpers/database/regexps"; +import * as goods from "../helpers/database/goods"; + +describe("Upload Good with specified categories", () => { + + let request: supertest.SuperTest; + + before(async () => { + request = await init(); + }); + + before(() => { + return db.connect(); + }); + + const ids = { + categories: [ ], + regexps: [ ], + users: [ ], + goods: [ ] + }; + after(() => { + return db.drop(ids); + }); + + const filepaths = [ ]; + after(() => { + return files.remove(filepaths); + }); + + let cids = [ ]; + before(async () => { + cids = await categories.addCategories(); + ids.categories.push(...cids); + }); + + before("login", async () => { + ids.users.push((await auth.login(request))[0]); + }); + + const targetIndex = 6; + + step("Set Category Regexp", async () => { + const targetId = ids.categories[targetIndex]; + + for (let i = 0; i < 2; i++) { + const filepath = files.newFile(); + filepaths.push(filepath); + const filename = path.basename(filepath); + + const regDoc = await regexps.newRegexp({ + name: newName(), + value: new RegExp(filename).source, + link: targetId, + hidden: true + }); + ids.regexps.push(regDoc._id); + } + }); + + step("Default Upload Way Fail", async () => { + const targetId = ids.categories[targetIndex]; + const filepath = filepaths[0]; + + const { status } = await files.uploadFile(request, filepath); + status.should.be.eql(400); + }); + + step("Upload Way Success with Specified Category", async () => { + const targetId = ids.categories[targetIndex]; + const filepath = filepaths[0]; + const filename = path.basename(filepath); + + const { status } = await files.uploadFile( + request, filepath, { query: { + "category": await categories.getNameById(targetId) + }} + ); + status.should.be.eql(201); + ids.goods.push(await goods.getIdByOriginname(filename)); + }); + + step("Upload Way Success with Specified Parent Category", async () => { + const targetId = ids.categories[targetIndex - 1]; + const filepath = filepaths[1]; + const filename = path.basename(filepath); + + const { status } = await files.uploadFile( + request, filepath, { query: { + "category": await categories.getNameById(targetId) + }} + ); + status.should.be.eql(201); + ids.goods.push(await goods.getIdByOriginname(filename)); + }); + +}); diff --git a/test/api/regexps.e2e.ts b/test/api/regexps.e2e.ts index d45101a..f2ab191 100644 --- a/test/api/regexps.e2e.ts +++ b/test/api/regexps.e2e.ts @@ -6,6 +6,8 @@ import { RegexpsService } from "@services/regexps"; import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; import { sleep } from "../helpers/utils"; +import { newName } from "./../helpers/utils"; +import auth = require("../helpers/database/auth"); describe("Regexp E2E Api", () => { @@ -34,28 +36,19 @@ describe("Regexp E2E Api", () => { regexpsSvr = new RegexpsService(); }); - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("Add 3 Regexps", async () => { const items = [{ - name: faker.random.word(), + name: newName(), value: "^list.0" }, { - name: faker.random.word(), + name: newName(), value: "^list.1" }, { - name: faker.random.word(), + name: newName(), value: "^list.2" }]; for (const item of items) { @@ -94,7 +87,7 @@ describe("Regexp E2E Api", () => { }); const data = { - name: faker.random.word(), + name: newName(), value: "^abc.ccd" }; step("Add Regexp", async () => { @@ -137,7 +130,7 @@ describe("Regexp E2E Api", () => { step("Modify Name", async () => { const raw = await RegexpsModel.addRegexp( - faker.random.word(), "^modify" + newName(), new RegExp(newName()).source ); ids.regexps.push(raw._id); const { body: result, status: status } = @@ -149,21 +142,22 @@ describe("Regexp E2E Api", () => { }); step("Modify Value", async () => { + const oldRegexp = new RegExp(newName()); + const newRegexp = new RegExp(newName()); const raw = await RegexpsModel.addRegexp( - faker.random.word(), "modify value" + newName(), oldRegexp.source ); ids.regexps.push(raw._id); - const { body: result, status: status } = - await request.post(`${URL}/${raw._id}`) - .send({ value: "^adb.ccd$" }).then(); + const { body: result, status } = await request.post(`${URL}/${raw._id}`) + .send({ value: newRegexp.source }).then(); status.should.be.eql(200); - const regexp = await RegexpsModel.findById(raw._id).exec(); - regexp.toObject().should.have.property("value", "^adb.ccd$"); + const regexpDoc = await RegexpsModel.findById(raw._id).exec(); + regexpDoc.toObject().should.have.property("value", newRegexp.source); }); step("Modify Exist Name", async () => { const raw = - await RegexpsModel.addRegexp(faker.random.word(), "^abc.ccd$"); + await RegexpsModel.addRegexp(newName(), "^abc.ccd$"); ids.regexps.push(raw._id); const { body: result, status: status } = await request.post(`${URL}/${raw._id}`) @@ -186,7 +180,7 @@ describe("Regexp E2E Api", () => { step("Modify with Empty Param", async () => { const raw = await RegexpsModel.addRegexp( - faker.random.word(), "^empty.param" + newName(), "^empty.param" ); ids.regexps.push(raw._id); const { body: result, status: status } = @@ -196,7 +190,7 @@ describe("Regexp E2E Api", () => { step("Delete Regexp By GET", async () => { const raw = await RegexpsModel.addRegexp( - faker.random.word(), "^get.delete" + newName(), "^get.delete" ); ids.regexps.push(raw._id); // Delete @@ -209,7 +203,7 @@ describe("Regexp E2E Api", () => { step("Delete Regexp By DELETE", async () => { const raw = await RegexpsModel.addRegexp( - faker.random.word(), "^delete.delete" + newName(), "^delete.delete" ); ids.regexps.push(raw._id); // Delete diff --git a/test/helpers/database.ts b/test/helpers/database.ts index 3a1cfdf..6292e1e 100644 --- a/test/helpers/database.ts +++ b/test/helpers/database.ts @@ -1,4 +1,3 @@ -import faker = require("faker"); import { config } from "@utils/config"; import { ObjectId } from "@models/common"; @@ -14,6 +13,9 @@ import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { connectDatabase } from "../../src/modules/database/database.providers"; import { newUser as newUserFn } from "./database/user"; +import * as regexps from "./database/regexps"; +import * as categories from "./database/categories"; +import { newName } from "./utils"; config.db.database = "storebox-test"; @@ -65,20 +67,21 @@ export const drop = async (ids?: IIds) => { } }; -export const addCategoryAndRegexp = async (regexp: RegExp) => { - const category = await CategoriesModel.create({ - name: faker.name.findName() +export const addCategoryAndRegexp = async (regexp: RegExp, pid?: ObjectId) => { + const category = await newCategory({ + name: newName(), + pid: pid }); - const reg = await newRegexp(faker.random.word(), regexp, category._id); - return [category, reg]; + const reg = await newRegexp(newName(), regexp, category._id); + return [ category, reg ]; }; export const newUser = newUserFn; -export const newRegexp = (name: string, value: RegExp, link?) => { - return RegexpsModel.addRegexp(name, value.source, link); +export const newRegexp = (name: string, value: RegExp, link?: ObjectId) => { + return regexps.newRegexp({ + name, value: value.source, link, hidden: false + }); }; -export const newCategory = (obj: object) => { - return CategoriesModel.create(obj) as Promise; -}; +export const newCategory = categories.newCategory; diff --git a/test/helpers/database/auth.ts b/test/helpers/database/auth.ts new file mode 100644 index 0000000..05a987e --- /dev/null +++ b/test/helpers/database/auth.ts @@ -0,0 +1,16 @@ +import supertest = require("supertest"); +import faker = require("faker"); +import { newUser } from "./user"; + +export const login = async ( + request: supertest.SuperTest, + username = `${faker.name.firstName()}${Math.random()}`, + password = `${faker.random.words()}${Math.random()}`, +) => { + const doc = await newUser(username, password); + await request.post("/api/v1/auth/login") + .send({ + username, password + }).then(); + return [ doc._id ]; +}; diff --git a/test/helpers/database/categories.ts b/test/helpers/database/categories.ts new file mode 100644 index 0000000..32f67aa --- /dev/null +++ b/test/helpers/database/categories.ts @@ -0,0 +1,52 @@ +import { ObjectId } from "@models/common"; +import { Model as CategoriesModel, CategoryDoc } from "@models/Categroy"; +import faker = require("faker"); + +export const newCategory = (obj: object) => { + return CategoriesModel.create(obj) as Promise; +}; + +/** + * Add 11 Categories + * ``` + * - 1 - 4 + * | + * - 0 - 2 - 5 - 6 - 8 + * | | | + * pid -| - 3 - 7 + * | + * - 9 - 10 + * ``` + * @param pid Parent Category ID + * @returns Categories' ID + */ +export const addCategories = async (pid?: ObjectId) => { + const cids: ObjectId[ ] = []; + // Create 11 Categories + for (let i = 0; i < 11; i++) { + const result = await CategoriesModel.create({ + name: faker.name.firstName() + i + }); + cids.push(result._id); + } + // [parent, child] + const initGroups = [ + [0, 1], [0, 2], [0, 3], + [1, 4], [2, 5], + [5, 6], [5, 7], [6, 8], + [9, 10] + ]; + for (const set of initGroups) { + await CategoriesModel.moveCategory(cids[set[1]], cids[set[0]]); + } + if (pid) { + await CategoriesModel.moveCategory(cids[9], pid); + await CategoriesModel.moveCategory(cids[0], pid); + } + return cids; +}; + +export const getNameById = async (id: ObjectId) => { + const doc = await CategoriesModel.findById(id).exec(); + return doc.toObject().name || undefined; +}; diff --git a/test/helpers/database/goods.ts b/test/helpers/database/goods.ts new file mode 100644 index 0000000..292b776 --- /dev/null +++ b/test/helpers/database/goods.ts @@ -0,0 +1,5 @@ +import { Model as GoodsModels } from "@models/Good"; + +export const getIdByOriginname = async (name: string) => { + return (await GoodsModels.findOne({ originname: name }).exec())._id; +}; diff --git a/test/helpers/database/regexps.ts b/test/helpers/database/regexps.ts new file mode 100644 index 0000000..1174b2f --- /dev/null +++ b/test/helpers/database/regexps.ts @@ -0,0 +1,16 @@ +import { IRegexpDoc } from "@models/Regexp"; +import { RegexpsService } from "@services/regexps"; + +let regexpSvr: RegexpsService; + +const init = () => { + if (!regexpSvr) { + regexpSvr = new RegexpsService(); + } + return regexpSvr; +}; + +export const newRegexp = (obj: IRegexpDoc) => { + init(); + return regexpSvr.create(obj); +}; diff --git a/test/helpers/database/usergroups.ts b/test/helpers/database/usergroups.ts index 2e5a39f..e779e6e 100644 --- a/test/helpers/database/usergroups.ts +++ b/test/helpers/database/usergroups.ts @@ -2,8 +2,7 @@ import faker = require("faker"); import { ObjectId } from "@models/common"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { UsergroupsService } from "@services/usergroups"; - -/// +import { newName } from "../utils"; export const getLinkIdsByUserId = async (uid: ObjectId) => { return (await UserUsergroupsModel.find({ user: uid }).exec()) @@ -20,7 +19,7 @@ export const getLinkIdsByUsergroupId = async (gid: ObjectId) => { }; export const newUsergroup = async ( - name = `${faker.random.word}${Math.random()}`, uid?: ObjectId + name = newName(), uid?: ObjectId ) => { const svr = new UsergroupsService(); const group = await svr.add({ name }); diff --git a/test/helpers/files.ts b/test/helpers/files.ts index f3bac33..c6123e1 100644 --- a/test/helpers/files.ts +++ b/test/helpers/files.ts @@ -1,19 +1,86 @@ import supertest = require("supertest"); +import { config } from "@utils/config"; +import { isArray } from "util"; +import faker = require("faker"); +import fs = require("fs-extra"); + +interface IUploadFileOptions { + query?: { + [key: string]: string + }; +} + +/* tslint:disable:no-empty-interface */ +interface IUploadFilesOptions extends IUploadFileOptions { } + +const addQuery = (url: string, opts: IUploadFileOptions) => { + if (opts.query && Object.keys(opts.query).length > 0) { + const query = [ ]; + for (const key of Object.keys(opts.query)) { + const q = opts.query[key].split(/\s+/).map((val) => { + return `${key}=${val}`; + }).join("&"); + query.push(q); + } + url += `?${query.join("&")}`; + } + return url; +}; export const uploadFile = ( - request: supertest.SuperTest, filepath: string + request: supertest.SuperTest, filepath: string, + opts: IUploadFileOptions = { } ) => { - return request.post("/api/v1/goods") - .attach("file", filepath) + let url = "/api/v1/goods"; + url = addQuery(url, opts); + + return request.post(url).attach("file", filepath) .then(); }; export const uploadFiles = ( - request: supertest.SuperTest, filepaths: string[] + request: supertest.SuperTest, filepaths: string[], + opts: IUploadFilesOptions = { } ) => { - let req = request.post("/api/v1/goods/collections"); + let url = "/api/v1/goods/collections"; + url = addQuery(url, opts); + + let req = request.post(url); filepaths.forEach((filepath) => { req = req.attach("files", filepath); }); return req.then(); }; + +const newFilename = () => { + const name = `${faker.random.word()}_${faker.random.uuid()}`; + const random = Math.random(); + const randomStr = `${random}`.replace(/(^0|\D)/g, ""); + return `${randomStr}-${name}`; +}; + +/** + * Generate File + * @param filename + * @returns filepath + */ +export const newFile = (filename = newFilename()) => { + const folderpath = `${config.paths.tmp}/test`; + if (!fs.existsSync(folderpath)) { + fs.mkdirpSync(folderpath); + } + const filepath = `${folderpath}/${filename}`; + fs.writeFileSync(filepath, JSON.stringify({ + data: Math.random() + }), { encoding: "utf-8" }); + return filepath; +}; + +export const remove = (filepaths: string[] | string) => { + if (!isArray(filepaths)) { + filepaths = [ filepaths ]; + } + return Promise.all(filepaths.map((filepath) => { + return fs.unlink(filepath); + })); +}; diff --git a/test/helpers/utils.ts b/test/helpers/utils.ts index 5ccfb06..3871ddd 100644 --- a/test/helpers/utils.ts +++ b/test/helpers/utils.ts @@ -1,5 +1,13 @@ +import faker = require("faker"); + export const sleep = (ms: number) => { return new Promise((reslove) => { setTimeout(reslove, ms); }); }; + +export const newName = (prefix = "") => { + const randomStr = `${Math.random()}`.replace(/(^0|\D)/g, ""); + const name = `${faker.random.word()}${randomStr}${Date.now()}`; + return `${prefix}${name}`; +}; diff --git a/test/models/Categroies.spec.ts b/test/models/Categroies.spec.ts index 001b116..52e9599 100644 --- a/test/models/Categroies.spec.ts +++ b/test/models/Categroies.spec.ts @@ -2,6 +2,7 @@ import { Model as CategoriesModel } from "@models/Categroy"; import { Model as ValuesModel } from "@models/Value"; import db = require("../helpers/database"); import faker = require("faker"); +import { addCategories } from "../helpers/database/categories"; describe("Category Model", () => { @@ -33,24 +34,8 @@ describe("Category Model", () => { let cids = [ ]; before(async () => { - cids = []; - for (let i = 0; i < 10; i++) { - const result = await CategoriesModel.create({ - name: faker.name.firstName() + i - }); - cids.push(result._id); - ids.categories.push(result._id); - } - // [parent, child] - const initGroups = [ - [0, 1], [0, 2], [0, 3], - [1, 4], [2, 5], - [5, 6], [6, 7], - [8, 9] - ]; - for (const set of initGroups) { - await CategoriesModel.moveCategory(cids[set[1]], cids[set[0]]); - } + cids = await addCategories(); + ids.categories.push(...cids); }); it("# 0", async () => { diff --git a/test/models/regexp.spec.ts b/test/models/regexp.spec.ts index 9445703..365680e 100644 --- a/test/models/regexp.spec.ts +++ b/test/models/regexp.spec.ts @@ -47,46 +47,48 @@ describe("RegExp Model", () => { should(reg).be.a.null(); }); - it("Link One Category And Undo", async () => { - const md5sum = md5(Date.now() + ""); - let reg: RegexpDoc; + it.skip("Link One Category And Undo", async () => { + // const md5sum = md5(Date.now() + ""); + // let reg: RegexpDoc; - reg = await RegexpsModel.addRegexp(md5sum, /[\da-fA-F]/.source); - ids.regexps.push(reg._id); - reg = await RegexpsModel.link(reg._id, Category._id); - reg = await RegexpsModel.link(reg._id, false); - reg = await RegexpsModel.findById(reg._id).exec(); + // reg = await RegexpsModel.addRegexp(md5sum, /[\da-fA-F]/.source); + // ids.regexps.push(reg._id); + // reg = await RegexpsModel.link(reg._id, Category._id); + // reg = await RegexpsModel.link(reg._id, false); + // reg = await RegexpsModel.findById(reg._id).exec(); - should(reg.toObject().link).be.a.undefined(); + // should(reg.toObject().link).be.a.undefined(); }); - it("Discern from No link Regexp", async () => { - const md5sum = md5(Date.now() + ""); - const regs = [ - await RegexpsModel.addRegexp(`${md5sum}1`, /[\da-fA-F]/.source), - await RegexpsModel.addRegexp(`${md5sum}2`, /[\da-fA-F]{16}/.source), - await RegexpsModel.addRegexp(`${md5sum}3`, /[\da-fA-F]{8}/.source) - ]; - for (const reg of regs) { - ids.regexps.push(reg._id); - } - const list = await RegexpsModel.discern(md5sum); - list.should.be.length(0); + // Discern Method move to Service + it.skip("Discern from No link Regexp", async () => { + // const md5sum = md5(Date.now() + ""); + // const regs = [ + // await RegexpsModel.addRegexp(`${md5sum}1`, /[\da-fA-F]/.source), + // await RegexpsModel.addRegexp(`${md5sum}2`, /[\da-fA-F]{16}/.source), + // await RegexpsModel.addRegexp(`${md5sum}3`, /[\da-fA-F]{8}/.source) + // ]; + // for (const reg of regs) { + // ids.regexps.push(reg._id); + // } + // const list = await RegexpsModel.discern(md5sum); + // list.should.be.length(0); }); - it("Discern from linked Regexps", async () => { - const md5sum = md5(Date.now() + ""); - const regs = [ - await RegexpsModel.addRegexp(`${md5sum}1`, /[\da-fA-F]/.source), - await RegexpsModel.addRegexp(`${md5sum}2`, /[\da-fA-F]{16}/.source), - await RegexpsModel.addRegexp(`${md5sum}3`, /[\da-fA-F]{8}/.source) - ]; - for (const reg of regs) { - ids.regexps.push(reg._id); - await RegexpsModel.link(reg._id, Category._id); - } - const list = await RegexpsModel.discern(md5sum); - list.should.be.length(3); + // Discern Method move to Service + it.skip("Discern from linked Regexps", async () => { + // const md5sum = md5(Date.now() + ""); + // const regs = [ + // await RegexpsModel.addRegexp(`${md5sum}1`, /[\da-fA-F]/.source), + // await RegexpsModel.addRegexp(`${md5sum}2`, /[\da-fA-F]{16}/.source), + // await RegexpsModel.addRegexp(`${md5sum}3`, /[\da-fA-F]{8}/.source) + // ]; + // for (const reg of regs) { + // ids.regexps.push(reg._id); + // await RegexpsModel.link(reg._id, Category._id); + // } + // const list = await RegexpsModel.discern(md5sum); + // list.should.be.length(3); }); }); From 55574458d0f3e19c0d5a364d1aec80b31c0c2d6a Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 8 Mar 2018 12:57:38 +0800 Subject: [PATCH 15/49] Add Append Category Test Unit , and update specified category test unit --- src/models/Regexp.ts | 2 +- test/api/goods_append.e2e.ts | 96 ++++++++++++++++++++++++ test/api/goods_specified_category.e2e.ts | 47 +++++++++++- 3 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 test/api/goods_append.e2e.ts diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index dbcb26f..c60881e 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -11,7 +11,7 @@ export const cache = Cache.create(`${Date.now()}${Math.random()}`); const Definition: SchemaDefinition = { name: { type: String, required: true, unique: true }, - value: { type: String, required: true, unique: true }, + value: { type: String, required: true }, link: { type: SchemaTypes.ObjectId, ref: CF diff --git a/test/api/goods_append.e2e.ts b/test/api/goods_append.e2e.ts new file mode 100644 index 0000000..20267ff --- /dev/null +++ b/test/api/goods_append.e2e.ts @@ -0,0 +1,96 @@ +import * as supertest from "supertest"; +import path = require("path"); +import { init } from "../helpers/server"; +import db = require("../helpers/database"); +import auth = require("../helpers/database/auth"); +import goods = require("../helpers/database/goods"); +import regexps = require("../helpers/database/regexps"); +import files = require("../helpers/files"); +import categories = require("../helpers/database/categories"); +import { newName } from "../helpers/utils"; + +describe("Upload Good with Append categories", () => { + + let request: supertest.SuperTest; + + before(async () => { + request = await init(); + }); + + before(() => { + return db.connect(); + }); + + const ids = { + categories: [ ], + regexps: [ ], + users: [ ], + goods: [ ] + }; + after(() => { + return db.drop(ids); + }); + + const filepaths = [ ]; + after(() => { + return files.remove(filepaths); + }); + + let cids = [ ]; + before(async () => { + cids = await categories.addCategories(); + ids.categories.push(...cids); + }); + + before("login", async () => { + ids.users.push((await auth.login(request))[0]); + }); + + step("Add File and its Regexps", async () => { + for (let i = 0; i < 2; i++) { + const filepath = files.newFile(); + filepaths.push(filepath); + const filename = path.basename(filepath); + + ids.regexps.push((await regexps.newRegexp({ + name: newName(), + value: new RegExp(filename).source, + link: ids.categories[6], + hidden: true + }))._id); + ids.regexps.push((await regexps.newRegexp({ + name: newName(), + value: new RegExp(filename).source, + link: ids.categories[7], + hidden: false + }))._id); + } + }); + + step("Upload File Fail", async () => { + const targetIndex = 6; + const targetId = ids.categories[targetIndex]; + const filepath = filepaths[0]; + const filename = path.basename(filepath); + + const { status } = await files.uploadFile( + request, filepath, { query: { + "append": await categories.getNameById(targetId) + }} + ); + status.should.be.eql(400); + // ids.goods.push(await goods.getIdByOriginname(filename)); + }); + + step("Upload File Success", async () => { + const targetIndex = 6; + const targetId = ids.categories[targetIndex]; + const filepath = filepaths[0]; + const filename = path.basename(filepath); + + const { status } = await files.uploadFile(request, filepath); + status.should.be.eql(201); + ids.goods.push(await goods.getIdByOriginname(filename)); + }); + +}); diff --git a/test/api/goods_specified_category.e2e.ts b/test/api/goods_specified_category.e2e.ts index 501aa7c..6272369 100644 --- a/test/api/goods_specified_category.e2e.ts +++ b/test/api/goods_specified_category.e2e.ts @@ -48,10 +48,10 @@ describe("Upload Good with specified categories", () => { const targetIndex = 6; - step("Set Category Regexp", async () => { + step("Set Category and Regexp", async () => { const targetId = ids.categories[targetIndex]; - for (let i = 0; i < 2; i++) { + for (let i = 0; i < 3; i++) { const filepath = files.newFile(); filepaths.push(filepath); const filename = path.basename(filepath); @@ -74,7 +74,7 @@ describe("Upload Good with specified categories", () => { status.should.be.eql(400); }); - step("Upload Way Success with Specified Category", async () => { + step("Upload File Success with Specified Category", async () => { const targetId = ids.categories[targetIndex]; const filepath = filepaths[0]; const filename = path.basename(filepath); @@ -88,7 +88,7 @@ describe("Upload Good with specified categories", () => { ids.goods.push(await goods.getIdByOriginname(filename)); }); - step("Upload Way Success with Specified Parent Category", async () => { + step("Upload File Success with Specified Parent Category", async () => { const targetId = ids.categories[targetIndex - 1]; const filepath = filepaths[1]; const filename = path.basename(filepath); @@ -102,4 +102,43 @@ describe("Upload Good with specified categories", () => { ids.goods.push(await goods.getIdByOriginname(filename)); }); + step("Upload File Fail with Specified Child Category", async () => { + const targetId = ids.categories[8]; + const filepath = filepaths[1]; + const filename = path.basename(filepath); + + const { status } = await files.uploadFile( + request, filepath, { query: { + "category": await categories.getNameById(targetId) + }} + ); + status.should.be.eql(400); + }); + + step("Upload File Fail with Specified Child Category", async () => { + const targetId = ids.categories[8]; + const filepath = filepaths[2]; + const filename = path.basename(filepath); + + const { status } = await files.uploadFile( + request, filepath, { query: { + "category": await categories.getNameById(targetId) + }} + ); + status.should.be.eql(400); + }); + + step("Upload File Fail with Specified Brother Category", async () => { + const targetId = ids.categories[7]; + const filepath = filepaths[2]; + const filename = path.basename(filepath); + + const { status } = await files.uploadFile( + request, filepath, { query: { + "category": await categories.getNameById(targetId) + }} + ); + status.should.be.eql(400); + }); + }); From fd57082c4dec145dc162caa9043956e36bd96911 Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 8 Mar 2018 13:20:35 +0800 Subject: [PATCH 16/49] Finish TODO --- TODOLIST.md | 4 ++-- src/models/Regexp.ts | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/TODOLIST.md b/TODOLIST.md index 38ff8a8..433517d 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -3,8 +3,8 @@ - [x] 用户组 - [ ] 权限 - [x] 默认用户组 -- [ ] 上传到指定Categroy - - [ ] 上传时追加Catogroy +- [x] 上传到指定Categroy + - [x] 上传时追加Catogroy - [ ] 整顿collectin info的goods 列表 - [ ] Token 使用日志显示 - [ ] Good 下载次数统计 diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index c60881e..f8314ad 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -97,7 +97,7 @@ RegexpSchema.path("value").validate({ RegexpSchema.path("value").validate({ isAsync: true, - validator: async function ValueExistValdator(value, respond) { + validator: async function ValueExistValidator(value, respond) { if (this && this.hidden) { return respond(true); } @@ -116,6 +116,26 @@ RegexpSchema.path("link").validate({ }, message: "The Category ID is not exist" }); + +RegexpSchema.path("hidden").validate({ + isAsync: true, + validator: async function hiddenExistValidator(value, respond) { + if (!value) { // hidden === false + return respond(true); + } + if (!this.isNew) { // hidden === Old Value + const id = this.getQuery()._id; + const col = await Model.findById(id).exec(); + if (col.toObject().hidden === value) { + return respond(true); + } + } + const result = + await Model.findOne({ value: this.value, hidden: value }).exec(); + respond(result ? false : true); + }, + message: "Only one active item with every value" +}); // endregion Validators for (const method of MODIFY_MOTHODS) { From 408b958c0d08a3f383b4d08f83c0802c03463f34 Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 8 Mar 2018 16:04:46 +0800 Subject: [PATCH 17/49] Update - add test command to travis script - add readme badge - upgrade `@nestjs/*` modules - use `test/tsconfig.json` on test process - update test units - remove its upload folder when remove category on test --- .travis.yml | 1 + README.md | 1 + TODOLIST.md | 2 +- package.json | 10 +++--- src/index.ts | 2 +- test/api/collections.e2e.ts | 46 ++++++++---------------- test/api/files.e2e.ts | 20 ++++------- test/api/goods_append.e2e.ts | 9 +++-- test/api/goods_specified_category.e2e.ts | 8 ++--- test/api/per_page.e2e.ts | 14 ++------ test/api/user_usergroup.e2e.ts | 22 +++--------- test/api/usergroups.e2e.ts | 17 +++------ test/api/users_goods.e2e.ts | 26 ++++++-------- test/helpers/database.ts | 7 ++++ test/helpers/database/goods.ts | 1 + test/helpers/database/user.ts | 2 +- test/helpers/files.ts | 9 +++-- test/issues/github_issue_16.e2e.ts | 15 +++----- test/issues/github_issue_21.e2e.ts | 12 +++---- test/issues/github_issue_22.e2e.ts | 14 ++++---- test/issues/github_issue_27.e2e.ts | 17 +++------ test/issues/github_issue_28.e2e.ts | 14 ++------ test/issues/github_issue_31.e2e.ts | 26 ++++---------- test/issues/github_issue_35.e2e.ts | 14 ++------ test/mocha.opts | 2 +- test/tsconfig.json | 7 ++-- 26 files changed, 115 insertions(+), 203 deletions(-) diff --git a/.travis.yml b/.travis.yml index f81505b..aee4e46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ install: - "npm install" script: + - "npm run tsc" - "npm test" after_success: diff --git a/README.md b/README.md index b28cae3..a2eb387 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Store PandoraBox Firmware Images and its packages [![Travis](https://img.shields.io/travis/Arylo/StoreBox.svg?style=flat-square)](https://travis-ci.org/Arylo/StoreBox) [![Coveralls](https://img.shields.io/coveralls/github/Arylo/StoreBox.svg?style=flat-square)](https://coveralls.io/github/Arylo/StoreBox) +[![Known Vulnerabilities](https://snyk.io/test/github/Arylo/StoreBox/badge.svg?style=flat-square)](https://snyk.io/test/github/Arylo/StoreBox) [![license](https://img.shields.io/github/license/Arylo/StoreBox.svg?style=flat-square)](https://github.com/Arylo/storebox) # Usage diff --git a/TODOLIST.md b/TODOLIST.md index 433517d..92dbcf1 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -9,7 +9,7 @@ - [ ] Token 使用日志显示 - [ ] Good 下载次数统计 - [ ] 接入统计 -- [ ] 配置文件写入初始化用户账号密码 +- [x] 配置文件写入初始化用户账号密码 - [ ] 接入AuthBox - [ ] Redis 接入 - [x] 支持Session diff --git a/package.json b/package.json index 7a5edd7..4bc41fc 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "start:dev": "npm run prestart && cross-env NODE_ENV=development npm run start-svr", "start:prod": "cross-env NODE_ENV=production npm run start-svr", "start": "npm run start:prod", - "test-spec": "cross-env NODE_ENV=test mocha", + "test-spec": "cross-env NODE_ENV=test TS_NODE_PROJECT=./test/tsconfig.json mocha", "test-cov": "nyc npm run test-spec && nyc report", "test": "npm run test-cov", "report-coverage": "cat ./coverage/lcov.info | coveralls" @@ -27,7 +27,7 @@ "author": "AryloYeung ", "license": "MIT", "devDependencies": { - "@nestjs/testing": "4.5.2", + "@nestjs/testing": "4.6.1", "@types/basic-auth": "^1.1.2", "@types/bunyan": "^1.8.4", "@types/connect-redis": "0.0.7", @@ -67,9 +67,9 @@ "typescript": "^2.6.1" }, "dependencies": { - "@nestjs/common": "4.5.2", - "@nestjs/core": "4.5.2", - "@nestjs/swagger": "^1.1.3", + "@nestjs/common": "4.6.5", + "@nestjs/core": "4.6.5", + "@nestjs/swagger": "1.1.4", "basic-auth": "^2.0.0", "body-parser": "^1.18.2", "bunyan": "^1.8.12", diff --git a/src/index.ts b/src/index.ts index 54dac33..8b085f1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ import { isDevelopment } from "./modules/common/helper/env"; const bootstrap = async () => { const server = initExpress(); - const app = await NestFactory.create(ApplicationModule, server); + const app = await NestFactory.create(ApplicationModule, server, { }); app.useGlobalPipes(new ValidationPipe()); if (isDevelopment) { diff --git a/test/api/collections.e2e.ts b/test/api/collections.e2e.ts index 3bf5f43..f94d9ec 100644 --- a/test/api/collections.e2e.ts +++ b/test/api/collections.e2e.ts @@ -11,6 +11,9 @@ import { import { init } from "../helpers/server"; import { uploadFiles } from "../helpers/files"; import { sleep } from "../helpers/utils"; +import auth = require("@db/auth"); +import goodsDb = require("@db/goods"); +import files = require("../helpers/files"); describe("Collections E2E Api", () => { @@ -24,7 +27,8 @@ describe("Collections E2E Api", () => { users: [ ], categories: [ ], regexps: [ ], - collections: [ ] + collections: [ ], + goods: [ ] }; after(() => { @@ -39,44 +43,20 @@ describe("Collections E2E Api", () => { const filepaths = [ ]; const prefix = `${faker.random.word()}_`; before(() => { - const folderpath = `${config.paths.tmp}/test`; - if (!fs.existsSync(folderpath)) { - fs.mkdirpSync(folderpath); - } // Generator Files for (let i = 0; i < FILE_COUNST; i++) { - const filepath = `${folderpath}/${prefix}${faker.random.uuid()}`; + const filename = `${prefix}${faker.random.uuid()}`; + const filepath = files.newFile(filename); filepaths.push(filepath); - fs.writeFileSync(filepath, JSON.stringify({ - data: Math.random() - }), { encoding: "utf-8" }); } }); - after(async () => { - for (const filepath of filepaths) { - fs.removeSync(filepath); - const good = (await GoodsModels.findOne({ - originname: basename(filepath) - }).exec()).toObject(); - fs.removeSync( - `${config.paths.upload}/${good.category}/${good.filename}` - ); - await GoodsModels.findByIdAndRemove(good._id).exec(); - } + after(() => { + return files.remove(filepaths); }); - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("Add Category and Regexp", async () => { @@ -94,6 +74,10 @@ describe("Collections E2E Api", () => { ids.collections.push(result._id); status.should.be.eql(201); result.should.have.properties("name", "_id", "goods"); + for (const good of result.goods) { + const originname = good.originname; + ids.goods.push(await goodsDb.getIdByOriginname(originname)); + } }); step("User's Collection List", async () => { diff --git a/test/api/files.e2e.ts b/test/api/files.e2e.ts index fb16ce2..ac81583 100644 --- a/test/api/files.e2e.ts +++ b/test/api/files.e2e.ts @@ -7,6 +7,7 @@ import { connect, drop, addCategoryAndRegexp, newUser } from "../helpers/databas import { uploadFile } from "../helpers/files"; import { sleep } from "../helpers/utils"; import { init, initWithAuth } from "../helpers/server"; +import auth = require("@db/auth"); describe("Files E2E Api", () => { @@ -35,17 +36,8 @@ describe("Files E2E Api", () => { return `/files/categories/${cid}/goods/${id}`; }; - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); let cid = ""; @@ -54,8 +46,10 @@ describe("Files E2E Api", () => { step("Upload File", async () => { const docs = await addCategoryAndRegexp(/^icon_.+64x64\.png$/); cid = docs[0]._id; - const uploadInfo = await uploadFile(request, uploadFilepath); - id = uploadInfo.body._id; + const { body: result, status } = + await uploadFile(request, uploadFilepath); + status.should.be.eql(201); + id = result._id; ids.categories.push(docs[0]._id); ids.regexps.push(docs[1]._id); ids.goods.push(id); diff --git a/test/api/goods_append.e2e.ts b/test/api/goods_append.e2e.ts index 20267ff..e3b9e7b 100644 --- a/test/api/goods_append.e2e.ts +++ b/test/api/goods_append.e2e.ts @@ -2,11 +2,11 @@ import * as supertest from "supertest"; import path = require("path"); import { init } from "../helpers/server"; import db = require("../helpers/database"); -import auth = require("../helpers/database/auth"); -import goods = require("../helpers/database/goods"); -import regexps = require("../helpers/database/regexps"); +import auth = require("@db/auth"); +import goods = require("@db/goods"); +import regexps = require("@db/regexps"); import files = require("../helpers/files"); -import categories = require("../helpers/database/categories"); +import categories = require("@db/categories"); import { newName } from "../helpers/utils"; describe("Upload Good with Append categories", () => { @@ -79,7 +79,6 @@ describe("Upload Good with Append categories", () => { }} ); status.should.be.eql(400); - // ids.goods.push(await goods.getIdByOriginname(filename)); }); step("Upload File Success", async () => { diff --git a/test/api/goods_specified_category.e2e.ts b/test/api/goods_specified_category.e2e.ts index 6272369..51964d0 100644 --- a/test/api/goods_specified_category.e2e.ts +++ b/test/api/goods_specified_category.e2e.ts @@ -2,12 +2,12 @@ import * as supertest from "supertest"; import db = require("../helpers/database"); import path = require("path"); import files = require("../helpers/files"); -import auth = require("../helpers/database/auth"); -import categories = require("../helpers/database/categories"); +import auth = require("@db/auth"); +import categories = require("@db/categories"); +import * as regexps from "@db/regexps"; +import * as goods from "@db/goods"; import { init } from "../helpers/server"; import { newName } from "../helpers/utils"; -import * as regexps from "../helpers/database/regexps"; -import * as goods from "../helpers/database/goods"; describe("Upload Good with specified categories", () => { diff --git a/test/api/per_page.e2e.ts b/test/api/per_page.e2e.ts index 11a466d..b8563bc 100644 --- a/test/api/per_page.e2e.ts +++ b/test/api/per_page.e2e.ts @@ -4,6 +4,7 @@ import { connect, drop, newUser, newRegexp, newCategory } from "../helpers/database"; import { init } from "../helpers/server"; +import auth = require("@db/auth"); describe("the E2E Api of display item count Per page", () => { @@ -26,17 +27,8 @@ describe("the E2E Api of display item count Per page", () => { request = await init(); }); - before("Login", async () => { - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); describe("Users", () => { diff --git a/test/api/user_usergroup.e2e.ts b/test/api/user_usergroup.e2e.ts index 1d611a2..01c292c 100644 --- a/test/api/user_usergroup.e2e.ts +++ b/test/api/user_usergroup.e2e.ts @@ -1,15 +1,11 @@ import supertest = require("supertest"); import faker = require("faker"); -import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; -import { UsersService } from "@services/users"; -import { SystemService } from "@services/system"; -import { UsergroupsService } from "@services/usergroups"; - import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; -import { newUsergroup, getLinkIdsByUserId, getLinkIdsByUsergroupId } from "../helpers/database/usergroups"; -import { newUser, newUserWithUsergroup } from "../helpers/database/user"; +import { newUsergroup } from "@db/usergroups"; +import { newUser, newUserWithUsergroup } from "@db/user"; +import auth = require("@db/auth"); describe("User's Usergroup E2E Api", () => { @@ -32,16 +28,8 @@ describe("User's Usergroup E2E Api", () => { request = await init(); }); - const user = { - username: `${faker.name.firstName()}${Math.random()}`, - password: faker.random.words() - }; - step("Login", async () => { - const userDoc = await newUserWithUsergroup( - user.username, user.password - ); - ids.users.push(userDoc._id); - await request.post("/api/v1/auth/login").send(user).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("Get Usergroup", async () => { diff --git a/test/api/usergroups.e2e.ts b/test/api/usergroups.e2e.ts index 057b57d..8f676cf 100644 --- a/test/api/usergroups.e2e.ts +++ b/test/api/usergroups.e2e.ts @@ -5,10 +5,8 @@ import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; -import { - newUsergroup, getLinkIdsByUserId -} from "../helpers/database/usergroups"; -import { newUser, newUserWithUsergroup } from "../helpers/database/user"; +import { newUsergroup } from "@db/usergroups"; +import auth = require("@db/auth"); describe("Usergroup E2E Api", () => { @@ -32,15 +30,8 @@ describe("Usergroup E2E Api", () => { request = await init(); }); - const user = { - username: `${faker.name.firstName()}${Math.random()}`, - password: faker.random.words() - }; - step("Login", async () => { - const userDoc = - await newUserWithUsergroup(user.username, user.password); - ids.users.push(userDoc._id); - await request.post("/api/v1/auth/login").send(user).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("New Usergroup * 2", async () => { diff --git a/test/api/users_goods.e2e.ts b/test/api/users_goods.e2e.ts index bcd0d49..6374b5d 100644 --- a/test/api/users_goods.e2e.ts +++ b/test/api/users_goods.e2e.ts @@ -5,6 +5,7 @@ import { connect, drop, newUser, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; +import auth = require("@db/auth"); describe("User's Goods E2E Api", () => { @@ -33,13 +34,11 @@ describe("User's Goods E2E Api", () => { pass: faker.random.words(), id: "" }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + + before("login", async () => { + const id = (await auth.login(request, user.name, user.pass))[0]; + ids.users.push(id); + user.id = id; }); step("Status Code isnt 500", async () => { @@ -61,14 +60,11 @@ describe("User's Goods E2E Api", () => { pass: faker.random.words(), id: "" }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - user.id = doc._id; - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + + before("login", async () => { + const id = (await auth.login(request, user.name, user.pass))[0]; + ids.users.push(id); + user.id = id; }); step("Status Code isnt 500", async () => { diff --git a/test/helpers/database.ts b/test/helpers/database.ts index 6292e1e..436ab31 100644 --- a/test/helpers/database.ts +++ b/test/helpers/database.ts @@ -16,6 +16,7 @@ import { newUser as newUserFn } from "./database/user"; import * as regexps from "./database/regexps"; import * as categories from "./database/categories"; import { newName } from "./utils"; +import { remove } from "./files"; config.db.database = "storebox-test"; @@ -64,6 +65,12 @@ export const drop = async (ids?: IIds) => { await UserUsergroupsModel.remove({ usergroup: id }).exec(); } } + if (method === "categories") { + await remove((ids[method] || [ ]).reduce((arr, id) => { + arr.push(`${config.paths.upload}/${id.toString()}`); + return arr; + }, [ ])); + } } }; diff --git a/test/helpers/database/goods.ts b/test/helpers/database/goods.ts index 292b776..2f8a21c 100644 --- a/test/helpers/database/goods.ts +++ b/test/helpers/database/goods.ts @@ -1,4 +1,5 @@ import { Model as GoodsModels } from "@models/Good"; +import { config } from "@utils/config"; export const getIdByOriginname = async (name: string) => { return (await GoodsModels.findOne({ originname: name }).exec())._id; diff --git a/test/helpers/database/user.ts b/test/helpers/database/user.ts index b7fb827..99e68c8 100644 --- a/test/helpers/database/user.ts +++ b/test/helpers/database/user.ts @@ -8,7 +8,7 @@ export const newUser = ( username = `${faker.name.firstName()}${Math.random()}`, password = `${faker.random.words()}${Math.random()}` ) => { - return UsersModel.addUser(username, password); + return newUserWithUsergroup(username, password); }; export const newUserWithUsergroup = ( diff --git a/test/helpers/files.ts b/test/helpers/files.ts index c6123e1..4a65162 100644 --- a/test/helpers/files.ts +++ b/test/helpers/files.ts @@ -3,6 +3,7 @@ import { config } from "@utils/config"; import { isArray } from "util"; import faker = require("faker"); import fs = require("fs-extra"); +import { sleep } from "./utils"; interface IUploadFileOptions { query?: { @@ -80,7 +81,11 @@ export const remove = (filepaths: string[] | string) => { if (!isArray(filepaths)) { filepaths = [ filepaths ]; } - return Promise.all(filepaths.map((filepath) => { - return fs.unlink(filepath); + if (filepaths.length === 0) { + return Promise.resolve(); + } + return Promise.all(filepaths.map(async (filepath) => { + await sleep(200); + return fs.existsSync(filepath) ? fs.remove(filepath) : null; })); }; diff --git a/test/issues/github_issue_16.e2e.ts b/test/issues/github_issue_16.e2e.ts index b87a6ac..ee4f390 100644 --- a/test/issues/github_issue_16.e2e.ts +++ b/test/issues/github_issue_16.e2e.ts @@ -5,6 +5,7 @@ import { connect, drop, newUser, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; +import auth = require("@db/auth"); /** * Fix [Issue 16](https://github.com/Arylo/StoreBox/issues/16) @@ -32,17 +33,9 @@ describe("Fix Issues", () => { }); describe("Github 16 [Overwrite Cache List Api]", () => { - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("Add Category and Regexp", async () => { diff --git a/test/issues/github_issue_21.e2e.ts b/test/issues/github_issue_21.e2e.ts index 5acc496..c438385 100644 --- a/test/issues/github_issue_21.e2e.ts +++ b/test/issues/github_issue_21.e2e.ts @@ -5,6 +5,7 @@ import { connect, drop, newUser, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; +import auth = require("@db/auth"); /** * About [Issue 21](https://github.com/Arylo/StoreBox/issues/21) @@ -35,13 +36,10 @@ describe("Fix Issues", () => { name: faker.name.firstName(), pass: faker.random.words() }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push( + (await auth.login(request, user.name, user.pass))[0] + ); }); step("Goods List status code is 200", async () => { diff --git a/test/issues/github_issue_22.e2e.ts b/test/issues/github_issue_22.e2e.ts index ede9585..60da2a1 100644 --- a/test/issues/github_issue_22.e2e.ts +++ b/test/issues/github_issue_22.e2e.ts @@ -2,9 +2,10 @@ import supertest = require("supertest"); import faker = require("faker"); import { - connect, drop, newUser, addCategoryAndRegexp + connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; +import auth = require("@db/auth"); /** * Fix [Issue 22](https://github.com/Arylo/StoreBox/issues/22) @@ -35,13 +36,10 @@ describe("Fix Issues", () => { name: faker.name.firstName(), pass: faker.random.words() }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push( + (await auth.login(request, user.name, user.pass))[0] + ); }); step("Status Code isnt 500 #0", async () => { diff --git a/test/issues/github_issue_27.e2e.ts b/test/issues/github_issue_27.e2e.ts index 24ad684..cdc2524 100644 --- a/test/issues/github_issue_27.e2e.ts +++ b/test/issues/github_issue_27.e2e.ts @@ -2,9 +2,10 @@ import supertest = require("supertest"); import faker = require("faker"); import { - connect, drop, newUser, newCategory + connect, drop, newCategory } from "../helpers/database"; import { init } from "../helpers/server"; +import auth = require("@db/auth"); /** * Fix [Issue 27](https://github.com/Arylo/StoreBox/issues/27) @@ -32,17 +33,9 @@ describe("Fix Issues", () => { }); describe("Github 27 ", () => { - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("Add Category", async () => { diff --git a/test/issues/github_issue_28.e2e.ts b/test/issues/github_issue_28.e2e.ts index 08edaca..9d2bcbb 100644 --- a/test/issues/github_issue_28.e2e.ts +++ b/test/issues/github_issue_28.e2e.ts @@ -5,6 +5,7 @@ import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; +import auth = require("@db/auth"); /** * Fix [Issue 28](https://github.com/Arylo/StoreBox/issues/28) @@ -33,17 +34,8 @@ describe("Fix Issues", () => { describe("Github 28 [User can ban oneself]", () => { - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("Ban self Fail", async () => { diff --git a/test/issues/github_issue_31.e2e.ts b/test/issues/github_issue_31.e2e.ts index 94e177c..3bfe6b8 100644 --- a/test/issues/github_issue_31.e2e.ts +++ b/test/issues/github_issue_31.e2e.ts @@ -11,6 +11,8 @@ import { init } from "../helpers/server"; import { uploadFile } from "../helpers/files"; import { config } from "@utils/config"; +import auth = require("@db/auth"); +import files = require("../helpers/files"); /** * Fix [Issue 31](https://github.com/Arylo/StoreBox/issues/31) @@ -42,18 +44,11 @@ describe("Fix Issues", () => { let filepath = ""; let filename = ""; before(() => { - const folderpath = `${config.paths.tmp}/test`; - if (!fs.existsSync(folderpath)) { - fs.mkdirpSync(folderpath); - } - filepath = `${folderpath}/${faker.random.uuid()}`; - fs.writeFileSync(filepath, JSON.stringify({ - data: Math.random() - }), { encoding: "utf-8" }); + filepath = files.newFile(); }); after(() => { - fs.removeSync(filepath); + return files.remove(filepath); }); after(() => { @@ -62,17 +57,8 @@ describe("Fix Issues", () => { }).exec(); }); - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - step("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("Add Category and Regexp", async () => { diff --git a/test/issues/github_issue_35.e2e.ts b/test/issues/github_issue_35.e2e.ts index f21522c..978e9cd 100644 --- a/test/issues/github_issue_35.e2e.ts +++ b/test/issues/github_issue_35.e2e.ts @@ -5,6 +5,7 @@ import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; +import auth = require("@db/auth"); /** * The Feature of Edit User @@ -32,17 +33,8 @@ describe("Fix Issues", () => { describe("Github 35 [The Feature of Edit User]", () => { - const user = { - name: faker.name.firstName(), - pass: faker.random.words() - }; - before("Login", async () => { - const doc = await newUser(user.name, user.pass); - ids.users.push(doc._id); - await request.post("/api/v1/auth/login") - .send({ - username: user.name, password: user.pass - }).then(); + before("login", async () => { + ids.users.push((await auth.login(request))[0]); }); step("Edit User's Nickname", async () => { diff --git a/test/mocha.opts b/test/mocha.opts index 9074855..9a2971a 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -6,7 +6,7 @@ --reporter mochawesome --reporter-options reportDir=./coverage/mochawesome --recursive ---timeout 10000 +--timeout 50000 --exit ./test/**/*.spec.ts ./test/**/*.e2e.ts diff --git a/test/tsconfig.json b/test/tsconfig.json index 0e68987..12f2f6b 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -18,13 +18,14 @@ "outDir": "../dist", "baseUrl": "../", "paths": { - "@utils/*": [ "./src/utils/*", "./dist/utils/*" ], - "@models/*": [ "./src/models/*", "./dist/models/*" ], + "@utils/*": [ "./src/utils/*" ], + "@models/*": [ "./src/models/*" ], "@decorators/*": [ "./src/modules/common/decorators/*.decorator" ], "@pipes/*": [ "./src/modules/common/pipes/*.pipe" ], "@guards/*": [ "./src/modules/common/guards/*.guard" ], "@dtos/*": [ "./src/modules/common/dtos/*.dto" ], - "@services/*": [ "./src/modules/common/services/*.service" ] + "@services/*": [ "./src/modules/common/services/*.service" ], + "@db/*": [ "./test/helpers/database/*" ] } }, "include": [ From 7b0f61b9edc905aa00badc15892ddd45ee095c50 Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 8 Mar 2018 16:41:30 +0800 Subject: [PATCH 18/49] Add to-array pipe test unit --- src/modules/common/pipes/to-array.pipe.ts | 19 +++--- test/pipes/to-array.spec.ts | 79 +++++++++++++++++++++++ 2 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 test/pipes/to-array.spec.ts diff --git a/src/modules/common/pipes/to-array.pipe.ts b/src/modules/common/pipes/to-array.pipe.ts index 776ccbd..6007559 100644 --- a/src/modules/common/pipes/to-array.pipe.ts +++ b/src/modules/common/pipes/to-array.pipe.ts @@ -9,24 +9,27 @@ export class ToArrayPipe implements PipeTransform { this.properties = properties; } - public async transform(value: any, metadata: ArgumentMetadata) { + public transform(value: any, metadata: ArgumentMetadata) { if (!value || isArray(value)) { return value; } + if (!isObject(value)) { + return [ value ]; + } if (this.properties.length === 0) { - if (isObject(value)) { - for (const key of Object.keys(value)) { - value[key] = [ value[key] ]; + for (const key of Object.keys(value)) { + if (isArray(value[key])) { + continue; } - } else { - return [ value ]; + value[key] = [ value[key] ]; } } else { for (const property of this.properties) { - if (!value[property] || isArray(value[property])) { + const val = value[property]; + if (!val || isArray(val)) { continue; } - value[property] = [ value[property] ]; + value[property] = [ val ]; } } return value; diff --git a/test/pipes/to-array.spec.ts b/test/pipes/to-array.spec.ts new file mode 100644 index 0000000..d897dc8 --- /dev/null +++ b/test/pipes/to-array.spec.ts @@ -0,0 +1,79 @@ +import { ToArrayPipe } from "@pipes/to-array"; + +describe("To Array Pipe Test Unit", () => { + + const fn = (value, ...properties: string[]) => { + return new ToArrayPipe(...properties).transform(value, undefined); + }; + + it("Empty Value and Empty Property", () => { + const value = fn(undefined); + should(value).be.a.undefined(); + }); + + it("Array and Empty Property #0", () => { + const value = fn([ ]); + value.should.be.eql([ ]); + }); + + it("Array and Empty Property #1", () => { + const value = fn([ "test" ]); + value.should.be.eql([ "test" ]); + }); + + it("Array and Empty Property #2", () => { + const value = fn([ 123 ]); + value.should.be.eql([ 123 ]); + }); + + it("String and Empty Property", () => { + const value = fn("foo"); + value.should.be.an.Array().which.eql([ "foo" ]); + }); + + it("Number and Empty Property", () => { + const value = fn(10086); + value.should.be.an.Array().which.eql([ 10086 ]); + }); + + let OBJ; + beforeEach(() => { + OBJ = { + "bar": "foo", + "baz": [ "zoo" ] + }; + }); + + it("Object and Empty Property", () => { + const obj: object = fn(OBJ); + obj.should.have.properties({ + "bar": [ "foo" ], + "baz": [ "zoo" ] + }); + }); + + it("Object and Property #0", () => { + const obj: object = fn(OBJ, "bar"); + obj.should.have.properties({ + "bar": [ "foo" ], + "baz": [ "zoo" ] + }); + }); + + it("Object and Property #1", () => { + const obj: object = fn(OBJ, "baz"); + obj.should.have.properties({ + "bar": "foo", + "baz": [ "zoo" ] + }); + }); + + it("Object and Non-exist field", () => { + const obj: object = fn(OBJ, "foo"); + obj.should.have.properties({ + "bar": "foo", + "baz": [ "zoo" ] + }); + }); + +}); From 29c36ce42d9894899d1df674a6ec0e6f83332e48 Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 8 Mar 2018 17:24:16 +0800 Subject: [PATCH 19/49] Fix Name repeat name when run test unit --- test/api/categroies.e2e.ts | 26 ++++++++++++------------- test/api/collections.e2e.ts | 6 +++--- test/api/files.e2e.ts | 1 - test/api/goods.e2e.ts | 1 - test/api/per_page.e2e.ts | 10 +++++----- test/api/regexps.e2e.ts | 3 +-- test/api/token.e2e.ts | 10 +++++----- test/api/token_login_logout.e2e.ts | 6 +++--- test/api/user_usergroup.e2e.ts | 1 - test/api/usergroups.e2e.ts | 6 +++--- test/api/users_goods.e2e.ts | 10 +++++----- test/helpers/database.ts | 3 ++- test/helpers/database/auth.ts | 6 +++--- test/helpers/database/categories.ts | 4 ++-- test/helpers/database/user.ts | 11 +++-------- test/helpers/database/usergroups.ts | 1 - test/issues/ban_user_n_its_token.e2e.ts | 6 +++--- test/issues/github_issue_16.e2e.ts | 1 - test/issues/github_issue_21.e2e.ts | 6 +++--- test/issues/github_issue_22.e2e.ts | 6 +++--- test/issues/github_issue_27.e2e.ts | 6 +++--- test/issues/github_issue_28.e2e.ts | 1 - test/issues/github_issue_30.spec.ts | 4 ++-- test/issues/github_issue_31.e2e.ts | 1 - test/issues/github_issue_35.e2e.ts | 4 ++-- test/models/Categroies.spec.ts | 4 ++-- test/models/regexp.spec.ts | 4 ++-- test/services/collections.spec.ts | 8 ++++---- test/services/users.spec.ts | 10 +++++----- 29 files changed, 77 insertions(+), 89 deletions(-) diff --git a/test/api/categroies.e2e.ts b/test/api/categroies.e2e.ts index 54b24cf..931a465 100644 --- a/test/api/categroies.e2e.ts +++ b/test/api/categroies.e2e.ts @@ -1,9 +1,9 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; import { login } from "../helpers/database/auth"; +import { newName } from "../helpers/utils"; describe("Categories E2E Api", () => { @@ -33,7 +33,7 @@ describe("Categories E2E Api", () => { step("Add Category", async () => { const ctx = { - name: faker.name.firstName() + name: newName() }; const { body: result } = await request.post("/api/v1/categories") .send(ctx) @@ -48,11 +48,11 @@ describe("Categories E2E Api", () => { step("Add Category with Tags", async () => { const ctx = { - name: faker.name.firstName(), + name: newName(), tags: [ - faker.random.words(), - faker.random.words(), - faker.random.words() + newName(), + newName(), + newName() ] }; const { body: result } = await request.post("/api/v1/categories") @@ -68,17 +68,17 @@ describe("Categories E2E Api", () => { step("Add Category with Attributes", async () => { const ctx = { - name: faker.name.firstName(), + name: newName(), attributes: [ { - key: faker.random.words(), - value: faker.random.words() + key: newName(), + value: newName() }, { - key: faker.random.words(), - value: faker.random.words() + key: newName(), + value: newName() }, { - key: faker.random.words(), - value: faker.random.words() + key: newName(), + value: newName() } ].map((item) => JSON.stringify(item)) }; diff --git a/test/api/collections.e2e.ts b/test/api/collections.e2e.ts index f94d9ec..4325224 100644 --- a/test/api/collections.e2e.ts +++ b/test/api/collections.e2e.ts @@ -10,7 +10,7 @@ import { } from "../helpers/database"; import { init } from "../helpers/server"; import { uploadFiles } from "../helpers/files"; -import { sleep } from "../helpers/utils"; +import { sleep, newName } from "../helpers/utils"; import auth = require("@db/auth"); import goodsDb = require("@db/goods"); import files = require("../helpers/files"); @@ -95,8 +95,8 @@ describe("Collections E2E Api", () => { step("Add Other User", async () => { const user = { - name: faker.name.firstName(), - pass: faker.random.words() + name: newName(), + pass: newName() }; const doc = await newUser(user.name, user.pass); ids.users.push(doc._id); diff --git a/test/api/files.e2e.ts b/test/api/files.e2e.ts index ac81583..5584563 100644 --- a/test/api/files.e2e.ts +++ b/test/api/files.e2e.ts @@ -1,6 +1,5 @@ import supertest = require("supertest"); import path = require("path"); -import faker = require("faker"); import { HttpStatus } from "@nestjs/common"; import { connect, drop, addCategoryAndRegexp, newUser } from "../helpers/database"; diff --git a/test/api/goods.e2e.ts b/test/api/goods.e2e.ts index 9932fdc..aeb4d8b 100644 --- a/test/api/goods.e2e.ts +++ b/test/api/goods.e2e.ts @@ -1,6 +1,5 @@ import supertest = require("supertest"); import path = require("path"); -import faker = require("faker"); import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { uploadFile } from "../helpers/files"; diff --git a/test/api/per_page.e2e.ts b/test/api/per_page.e2e.ts index b8563bc..c364546 100644 --- a/test/api/per_page.e2e.ts +++ b/test/api/per_page.e2e.ts @@ -1,10 +1,10 @@ -import * as faker from "faker"; import supertest = require("supertest"); import { connect, drop, newUser, newRegexp, newCategory } from "../helpers/database"; import { init } from "../helpers/server"; import auth = require("@db/auth"); +import { newName } from "../helpers/utils"; describe("the E2E Api of display item count Per page", () => { @@ -70,8 +70,8 @@ describe("the E2E Api of display item count Per page", () => { step("Add 100 Users", async () => { for (let i = 0; i < 100; i++) { const user = { - name: i + faker.name.firstName(), - pass: i + faker.random.words() + name: newName(), + pass: newName() }; const doc = await newUser(user.name, user.pass); ids.users.push(doc._id); @@ -196,7 +196,7 @@ describe("the E2E Api of display item count Per page", () => { step("Add 100 Regexps", async () => { for (let i = 0; i < 100; i++) { const regexp = { - name: i + faker.name.firstName(), + name: newName(), regexp: "^regexp." + i }; const doc = await newRegexp( @@ -323,7 +323,7 @@ describe("the E2E Api of display item count Per page", () => { step("Add 100 categories", async () => { for (let i = 0; i < 100; i++) { const cate = { - name: i + faker.random.words() + name: newName() }; const doc = await newCategory(cate); ids.categories.push(doc._id); diff --git a/test/api/regexps.e2e.ts b/test/api/regexps.e2e.ts index f2ab191..514b0d4 100644 --- a/test/api/regexps.e2e.ts +++ b/test/api/regexps.e2e.ts @@ -1,5 +1,4 @@ import supertest = require("supertest"); -import faker = require("faker"); import { Model as RegexpsModel } from "@models/Regexp"; import { RegexpsService } from "@services/regexps"; @@ -167,7 +166,7 @@ describe("Regexp E2E Api", () => { step("Modify Exist Value", async () => { const data = { - name: faker.random.word(), + name: newName(), value: "^modify.exist.value" }; const raw = await RegexpsModel.addRegexp(data.name, data.value); diff --git a/test/api/token.e2e.ts b/test/api/token.e2e.ts index dbc4a76..3944174 100644 --- a/test/api/token.e2e.ts +++ b/test/api/token.e2e.ts @@ -1,9 +1,9 @@ import supertest = require("supertest"); -import faker = require("faker"); import { TokensService } from "@services/tokens"; import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; +import { newName } from "../helpers/utils"; describe("Token E2E Test", () => { @@ -28,12 +28,12 @@ describe("Token E2E Test", () => { }); const users = [{ - name: faker.name.firstName(), - pass: faker.random.words(), + name: newName(), + pass: newName(), id: "" }, { - name: faker.name.firstName(), - pass: faker.random.words(), + name: newName(), + pass: newName(), id: "" }]; diff --git a/test/api/token_login_logout.e2e.ts b/test/api/token_login_logout.e2e.ts index 57db3d2..9b32b14 100644 --- a/test/api/token_login_logout.e2e.ts +++ b/test/api/token_login_logout.e2e.ts @@ -1,9 +1,9 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; import { Model as TokensModel } from "@models/Token"; +import { newName } from "../helpers/utils"; describe("Token E2E Api", () => { @@ -27,8 +27,8 @@ describe("Token E2E Api", () => { }); const user = { - name: faker.name.firstName(), - pass: faker.random.words(), + name: newName(), + pass: newName(), id: "", token: "" }; diff --git a/test/api/user_usergroup.e2e.ts b/test/api/user_usergroup.e2e.ts index 01c292c..df357d7 100644 --- a/test/api/user_usergroup.e2e.ts +++ b/test/api/user_usergroup.e2e.ts @@ -1,5 +1,4 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; diff --git a/test/api/usergroups.e2e.ts b/test/api/usergroups.e2e.ts index 8f676cf..95a9225 100644 --- a/test/api/usergroups.e2e.ts +++ b/test/api/usergroups.e2e.ts @@ -1,5 +1,4 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, addCategoryAndRegexp @@ -7,6 +6,7 @@ import { import { init } from "../helpers/server"; import { newUsergroup } from "@db/usergroups"; import auth = require("@db/auth"); +import { newName } from "../helpers/utils"; describe("Usergroup E2E Api", () => { @@ -37,7 +37,7 @@ describe("Usergroup E2E Api", () => { step("New Usergroup * 2", async () => { for (let i = 0; i < 2; i++) { const url = `/api/v1/usergroups`; - const name = `${faker.random.word()}${Math.random()}`; + const name = newName(); const { status, body: result } = await request.post(url).send({ name }).then(); status.should.be.eql(201); @@ -89,7 +89,7 @@ describe("Usergroup E2E Api", () => { step("Modify Usergroup's name", async () => { const id = ids.usergroups[ids.usergroups.length - 1]; const url = `/api/v1/usergroups/${id}`; - const name = `${faker.random.word()}${Math.random()}`; + const name = newName(); const { status } = await request.post(url).send({ name }).then(); status.should.be.eql(200); const { body: result } = await request.get(url).then(); diff --git a/test/api/users_goods.e2e.ts b/test/api/users_goods.e2e.ts index 6374b5d..fc64a60 100644 --- a/test/api/users_goods.e2e.ts +++ b/test/api/users_goods.e2e.ts @@ -1,11 +1,11 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newUser, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; import auth = require("@db/auth"); +import { newName } from "../helpers/utils"; describe("User's Goods E2E Api", () => { @@ -30,8 +30,8 @@ describe("User's Goods E2E Api", () => { describe("Self Goods", () => { const user = { - name: faker.name.firstName(), - pass: faker.random.words(), + name: newName(), + pass: newName(), id: "" }; @@ -56,8 +56,8 @@ describe("User's Goods E2E Api", () => { describe("User's Goods", () => { const user = { - name: faker.name.firstName(), - pass: faker.random.words(), + name: newName(), + pass: newName(), id: "" }; diff --git a/test/helpers/database.ts b/test/helpers/database.ts index 436ab31..1137463 100644 --- a/test/helpers/database.ts +++ b/test/helpers/database.ts @@ -15,7 +15,7 @@ import { connectDatabase } from "../../src/modules/database/database.providers"; import { newUser as newUserFn } from "./database/user"; import * as regexps from "./database/regexps"; import * as categories from "./database/categories"; -import { newName } from "./utils"; +import { newName, sleep } from "./utils"; import { remove } from "./files"; config.db.database = "storebox-test"; @@ -36,6 +36,7 @@ interface IIds { export const connect = connectDatabase; export const drop = async (ids?: IIds) => { + await sleep(250); if (!ids) { await ValuesModel.remove({ }).exec(); await GoodsModels.remove({ }).exec(); diff --git a/test/helpers/database/auth.ts b/test/helpers/database/auth.ts index 05a987e..0e85513 100644 --- a/test/helpers/database/auth.ts +++ b/test/helpers/database/auth.ts @@ -1,11 +1,11 @@ import supertest = require("supertest"); -import faker = require("faker"); import { newUser } from "./user"; +import { newName } from "../utils"; export const login = async ( request: supertest.SuperTest, - username = `${faker.name.firstName()}${Math.random()}`, - password = `${faker.random.words()}${Math.random()}`, + username = newName(), + password = newName() ) => { const doc = await newUser(username, password); await request.post("/api/v1/auth/login") diff --git a/test/helpers/database/categories.ts b/test/helpers/database/categories.ts index 32f67aa..008203c 100644 --- a/test/helpers/database/categories.ts +++ b/test/helpers/database/categories.ts @@ -1,6 +1,6 @@ import { ObjectId } from "@models/common"; import { Model as CategoriesModel, CategoryDoc } from "@models/Categroy"; -import faker = require("faker"); +import { newName } from "../utils"; export const newCategory = (obj: object) => { return CategoriesModel.create(obj) as Promise; @@ -25,7 +25,7 @@ export const addCategories = async (pid?: ObjectId) => { // Create 11 Categories for (let i = 0; i < 11; i++) { const result = await CategoriesModel.create({ - name: faker.name.firstName() + i + name: newName() }); cids.push(result._id); } diff --git a/test/helpers/database/user.ts b/test/helpers/database/user.ts index 99e68c8..d45dac0 100644 --- a/test/helpers/database/user.ts +++ b/test/helpers/database/user.ts @@ -2,19 +2,14 @@ import { Model as UsersModel } from "@models/User"; import { ObjectId } from "@models/common"; import { UsersService } from "@services/users"; import { SystemService } from "@services/system"; -import faker = require("faker"); +import { newName } from "../utils"; -export const newUser = ( - username = `${faker.name.firstName()}${Math.random()}`, - password = `${faker.random.words()}${Math.random()}` -) => { +export const newUser = (username?: string, password?: string) => { return newUserWithUsergroup(username, password); }; export const newUserWithUsergroup = ( - username = `${faker.name.firstName()}${Math.random()}`, - password = `${faker.random.words()}${Math.random()}`, - gid?: ObjectId + username = newName(), password = newName(), gid?: ObjectId ) => { return new UsersService(new SystemService()).addUser({ username, password diff --git a/test/helpers/database/usergroups.ts b/test/helpers/database/usergroups.ts index e779e6e..08ca93d 100644 --- a/test/helpers/database/usergroups.ts +++ b/test/helpers/database/usergroups.ts @@ -1,4 +1,3 @@ -import faker = require("faker"); import { ObjectId } from "@models/common"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { UsergroupsService } from "@services/usergroups"; diff --git a/test/issues/ban_user_n_its_token.e2e.ts b/test/issues/ban_user_n_its_token.e2e.ts index 5d3e5e9..50c7f3d 100644 --- a/test/issues/ban_user_n_its_token.e2e.ts +++ b/test/issues/ban_user_n_its_token.e2e.ts @@ -1,11 +1,11 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; import { UsersService } from "@services/users"; import { TokensService } from "@services/tokens"; import { SystemService } from "@services/system"; +import { newName } from "../helpers/utils"; describe("Fix Issues", () => { @@ -31,8 +31,8 @@ describe("Fix Issues", () => { }); const user = { - name: faker.name.firstName(), - pass: faker.random.words(), + name: newName(), + pass: newName(), token: "" }; describe("Token Action When User ban", () => { diff --git a/test/issues/github_issue_16.e2e.ts b/test/issues/github_issue_16.e2e.ts index ee4f390..fc3e4e4 100644 --- a/test/issues/github_issue_16.e2e.ts +++ b/test/issues/github_issue_16.e2e.ts @@ -1,5 +1,4 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newUser, addCategoryAndRegexp diff --git a/test/issues/github_issue_21.e2e.ts b/test/issues/github_issue_21.e2e.ts index c438385..76fccdb 100644 --- a/test/issues/github_issue_21.e2e.ts +++ b/test/issues/github_issue_21.e2e.ts @@ -1,11 +1,11 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newUser, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; import auth = require("@db/auth"); +import { newName } from "../helpers/utils"; /** * About [Issue 21](https://github.com/Arylo/StoreBox/issues/21) @@ -33,8 +33,8 @@ describe("Fix Issues", () => { describe("Github 21", () => { const user = { - name: faker.name.firstName(), - pass: faker.random.words() + name: newName(), + pass: newName() }; before("login", async () => { ids.users.push( diff --git a/test/issues/github_issue_22.e2e.ts b/test/issues/github_issue_22.e2e.ts index 60da2a1..710fc57 100644 --- a/test/issues/github_issue_22.e2e.ts +++ b/test/issues/github_issue_22.e2e.ts @@ -1,11 +1,11 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; import { init } from "../helpers/server"; import auth = require("@db/auth"); +import { newName } from "../helpers/utils"; /** * Fix [Issue 22](https://github.com/Arylo/StoreBox/issues/22) @@ -33,8 +33,8 @@ describe("Fix Issues", () => { describe("Github 22", () => { const user = { - name: faker.name.firstName(), - pass: faker.random.words() + name: newName(), + pass: newName() }; before("login", async () => { ids.users.push( diff --git a/test/issues/github_issue_27.e2e.ts b/test/issues/github_issue_27.e2e.ts index cdc2524..0f5c401 100644 --- a/test/issues/github_issue_27.e2e.ts +++ b/test/issues/github_issue_27.e2e.ts @@ -1,11 +1,11 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newCategory } from "../helpers/database"; import { init } from "../helpers/server"; import auth = require("@db/auth"); +import { newName } from "../helpers/utils"; /** * Fix [Issue 27](https://github.com/Arylo/StoreBox/issues/27) @@ -39,7 +39,7 @@ describe("Fix Issues", () => { }); step("Add Category", async () => { - const doc = await newCategory({ name: faker.random.word() }); + const doc = await newCategory({ name: newName() }); ids.categories.push(doc._id); }); @@ -48,7 +48,7 @@ describe("Fix Issues", () => { body: result, status } = await request.post("/api/v1/regexps") .send({ - name: faker.random.word() + "link_cate", + name: newName(), value: new RegExp("chchachc.+").source, link: ids.categories[0] }).then(); diff --git a/test/issues/github_issue_28.e2e.ts b/test/issues/github_issue_28.e2e.ts index 9d2bcbb..4530b0d 100644 --- a/test/issues/github_issue_28.e2e.ts +++ b/test/issues/github_issue_28.e2e.ts @@ -1,5 +1,4 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newUser diff --git a/test/issues/github_issue_30.spec.ts b/test/issues/github_issue_30.spec.ts index 5add94e..605bf3f 100644 --- a/test/issues/github_issue_30.spec.ts +++ b/test/issues/github_issue_30.spec.ts @@ -1,7 +1,7 @@ import isRegExp = require("@utils/isRegExp"); import { connect } from "../helpers/database"; import { Model as RegexpsModel } from "@models/Regexp"; -import * as faker from "faker"; +import { newName } from "../helpers/utils"; /** * Fix [Issue 30](https://github.com/Arylo/StoreBox/issues/30) @@ -27,7 +27,7 @@ describe("Fix Issues", () => { it("Generate wrong Regexp item", async () => { try { await RegexpsModel.create({ - name: faker.random.word(), + name: newName(), value: "*" }); } catch (error) { diff --git a/test/issues/github_issue_31.e2e.ts b/test/issues/github_issue_31.e2e.ts index 3bfe6b8..525de48 100644 --- a/test/issues/github_issue_31.e2e.ts +++ b/test/issues/github_issue_31.e2e.ts @@ -1,5 +1,4 @@ import supertest = require("supertest"); -import faker = require("faker"); import fs = require("fs-extra"); import { basename } from "path"; diff --git a/test/issues/github_issue_35.e2e.ts b/test/issues/github_issue_35.e2e.ts index 978e9cd..1280e3f 100644 --- a/test/issues/github_issue_35.e2e.ts +++ b/test/issues/github_issue_35.e2e.ts @@ -1,11 +1,11 @@ import supertest = require("supertest"); -import faker = require("faker"); import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; import auth = require("@db/auth"); +import { newName } from "../helpers/utils"; /** * The Feature of Edit User @@ -38,7 +38,7 @@ describe("Fix Issues", () => { }); step("Edit User's Nickname", async () => { - const nickname = faker.name.firstName(); + const nickname = newName(); const id = ids.users[0]; const { status } = await request.post(`/api/v1/users/${id}`) .send({ nickname }) diff --git a/test/models/Categroies.spec.ts b/test/models/Categroies.spec.ts index 52e9599..c9061ae 100644 --- a/test/models/Categroies.spec.ts +++ b/test/models/Categroies.spec.ts @@ -1,8 +1,8 @@ import { Model as CategoriesModel } from "@models/Categroy"; import { Model as ValuesModel } from "@models/Value"; import db = require("../helpers/database"); -import faker = require("faker"); import { addCategories } from "../helpers/database/categories"; +import { newName } from "../helpers/utils"; describe("Category Model", () => { @@ -19,7 +19,7 @@ describe("Category Model", () => { it("Add Category", async () => { const ctx = { - name: faker.name.firstName() + name: newName() }; const obj = await CategoriesModel.create(ctx); ids.categories.push(obj._id); diff --git a/test/models/regexp.spec.ts b/test/models/regexp.spec.ts index 365680e..a2ecd1c 100644 --- a/test/models/regexp.spec.ts +++ b/test/models/regexp.spec.ts @@ -1,8 +1,8 @@ import * as db from "../helpers/database"; import * as md5 from "md5"; -import * as faker from "faker"; import { Model as RegexpsModel, RegexpDoc } from "@models/Regexp"; import { Model as CategoryModel, ICategoryRaw } from "@models/Categroy"; +import { newName } from "../helpers/utils"; describe("RegExp Model", () => { @@ -14,7 +14,7 @@ describe("RegExp Model", () => { beforeEach(async () => { const result = await CategoryModel.create({ - name: faker.name.findName() + name: newName() }); ids.categories.push(result._id); Category = result.toObject() as ICategoryRaw; diff --git a/test/services/collections.spec.ts b/test/services/collections.spec.ts index 6531adb..d9efd61 100644 --- a/test/services/collections.spec.ts +++ b/test/services/collections.spec.ts @@ -1,7 +1,7 @@ import { CollectionsService } from "@services/collections"; import { Model as UsersModel } from "@models/User"; import db = require("../helpers/database"); -import faker = require("faker"); +import { newName } from "../helpers/utils"; describe("Collections Service Test Unit", () => { @@ -19,7 +19,7 @@ describe("Collections Service Test Unit", () => { const user = await UsersModel.findOne().exec(); try { await collectionsSvr.create({ - name: faker.random.word(), + name: newName(), creator: user._id }); } catch (error) { @@ -31,7 +31,7 @@ describe("Collections Service Test Unit", () => { const user = await UsersModel.findOne().exec(); try { await collectionsSvr.create({ - name: faker.random.word(), + name: newName(), goods: [ ], creator: user._id }); @@ -44,7 +44,7 @@ describe("Collections Service Test Unit", () => { const user = await UsersModel.findOne().exec(); try { await collectionsSvr.create({ - name: faker.random.word(), + name: newName(), goods: [ "5a77c24ec1ae19d4a808e134" ], creator: user._id }); diff --git a/test/services/users.spec.ts b/test/services/users.spec.ts index 56df9ab..f6186a9 100644 --- a/test/services/users.spec.ts +++ b/test/services/users.spec.ts @@ -2,8 +2,8 @@ import { UsersService } from "@services/users"; import { SystemService } from "@services/system"; import { Model as UsersModel } from "@models/User"; import db = require("../helpers/database"); -import faker = require("faker"); import { newUser } from "../helpers/database"; +import { newName } from "../helpers/utils"; describe("Users Service Test Unit", () => { @@ -25,8 +25,8 @@ describe("Users Service Test Unit", () => { }); const user = { - name: faker.name.firstName(), - pass: faker.random.words() + name: newName(), + pass: newName() }; before(async () => { const doc = await newUser(user.name, user.pass); @@ -35,13 +35,13 @@ describe("Users Service Test Unit", () => { it("Cant Modify Username", async () => { const id = ids.users[0]; - usersSvr.modify(id, { username: faker.name.firstName() }) + usersSvr.modify(id, { username: newName() }) .should.be.rejected(); }); it("Modify nickname", async () => { const id = ids.users[0]; - usersSvr.modify(id, { nickname: faker.name.firstName() }) + usersSvr.modify(id, { nickname: newName() }) .should.be.fulfilled(); }); From 6b3a666c10cdb09441870f9ee5bec8b4bbf9e27d Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 8 Mar 2018 17:54:27 +0800 Subject: [PATCH 20/49] delay any second because new file sometime --- test/api/collections.e2e.ts | 4 ++-- test/api/collections_token.e2e.ts | 4 ++-- test/api/goods_append.e2e.ts | 4 ++-- test/api/goods_specified_category.e2e.ts | 2 +- test/helpers/files.ts | 3 ++- test/issues/github_issue_31.e2e.ts | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/test/api/collections.e2e.ts b/test/api/collections.e2e.ts index 4325224..cee0ef8 100644 --- a/test/api/collections.e2e.ts +++ b/test/api/collections.e2e.ts @@ -42,11 +42,11 @@ describe("Collections E2E Api", () => { const FILE_COUNST = 10; const filepaths = [ ]; const prefix = `${faker.random.word()}_`; - before(() => { + before(async () => { // Generator Files for (let i = 0; i < FILE_COUNST; i++) { const filename = `${prefix}${faker.random.uuid()}`; - const filepath = files.newFile(filename); + const filepath = await files.newFile(filename); filepaths.push(filepath); } }); diff --git a/test/api/collections_token.e2e.ts b/test/api/collections_token.e2e.ts index 14a3f73..c1ebdec 100644 --- a/test/api/collections_token.e2e.ts +++ b/test/api/collections_token.e2e.ts @@ -39,10 +39,10 @@ describe("Token to Upload Files Api", () => { const FILE_COUNST = 10; const filepaths = [ ]; const prefix = `${faker.random.word()}_`; - before(() => { + before(async () => { // Generator Files for (let i = 0; i < FILE_COUNST; i++) { - filepaths.push(newFile(`${prefix}${faker.random.uuid()}`)); + filepaths.push(await newFile(`${prefix}${faker.random.uuid()}`)); } }); diff --git a/test/api/goods_append.e2e.ts b/test/api/goods_append.e2e.ts index e3b9e7b..7ee594a 100644 --- a/test/api/goods_append.e2e.ts +++ b/test/api/goods_append.e2e.ts @@ -48,7 +48,7 @@ describe("Upload Good with Append categories", () => { step("Add File and its Regexps", async () => { for (let i = 0; i < 2; i++) { - const filepath = files.newFile(); + const filepath = await files.newFile(); filepaths.push(filepath); const filename = path.basename(filepath); @@ -73,7 +73,7 @@ describe("Upload Good with Append categories", () => { const filepath = filepaths[0]; const filename = path.basename(filepath); - const { status } = await files.uploadFile( + const { status, body: result } = await files.uploadFile( request, filepath, { query: { "append": await categories.getNameById(targetId) }} diff --git a/test/api/goods_specified_category.e2e.ts b/test/api/goods_specified_category.e2e.ts index 51964d0..946e6c7 100644 --- a/test/api/goods_specified_category.e2e.ts +++ b/test/api/goods_specified_category.e2e.ts @@ -52,7 +52,7 @@ describe("Upload Good with specified categories", () => { const targetId = ids.categories[targetIndex]; for (let i = 0; i < 3; i++) { - const filepath = files.newFile(); + const filepath = await files.newFile(); filepaths.push(filepath); const filename = path.basename(filepath); diff --git a/test/helpers/files.ts b/test/helpers/files.ts index 4a65162..ba82866 100644 --- a/test/helpers/files.ts +++ b/test/helpers/files.ts @@ -65,7 +65,7 @@ const newFilename = () => { * @param filename * @returns filepath */ -export const newFile = (filename = newFilename()) => { +export const newFile = async (filename = newFilename()) => { const folderpath = `${config.paths.tmp}/test`; if (!fs.existsSync(folderpath)) { fs.mkdirpSync(folderpath); @@ -74,6 +74,7 @@ export const newFile = (filename = newFilename()) => { fs.writeFileSync(filepath, JSON.stringify({ data: Math.random() }), { encoding: "utf-8" }); + await sleep(200); return filepath; }; diff --git a/test/issues/github_issue_31.e2e.ts b/test/issues/github_issue_31.e2e.ts index 525de48..1a7997a 100644 --- a/test/issues/github_issue_31.e2e.ts +++ b/test/issues/github_issue_31.e2e.ts @@ -42,8 +42,8 @@ describe("Fix Issues", () => { let filepath = ""; let filename = ""; - before(() => { - filepath = files.newFile(); + before(async () => { + filepath = await files.newFile(); }); after(() => { From 1b06cc23e1c89425c06af69abecc770add610774 Mon Sep 17 00:00:00 2001 From: Arylo Date: Sun, 11 Mar 2018 09:38:58 +0800 Subject: [PATCH 21/49] Fix Service Cache Fail --- src/modules/common/services/base.ts | 30 ++++++++++++++++ .../common/services/categories.service.ts | 16 ++++----- .../common/services/regexps.service.ts | 35 ++++++++----------- test/helpers/database/categories.ts | 3 +- test/tsconfig.json | 1 + tsconfig.json | 1 + 6 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 src/modules/common/services/base.ts diff --git a/src/modules/common/services/base.ts b/src/modules/common/services/base.ts new file mode 100644 index 0000000..1362a44 --- /dev/null +++ b/src/modules/common/services/base.ts @@ -0,0 +1,30 @@ +type TimeType = number | string; + +export abstract class BaseService { + + private cache; + + protected setCache(cache) { + this.cache = cache; + } + + protected loadAndCache( + FLAG: string, value: () => T, time?: TimeType + ): T { + if (!this.cache) { + return value(); + } + const c = this.cache.get(FLAG); + if (c) { + return c; + } + const val = value(); + if (time) { + this.cache.put(FLAG, val, time); + } else { + this.cache.put(FLAG, val); + } + return val; + } + +} diff --git a/src/modules/common/services/categories.service.ts b/src/modules/common/services/categories.service.ts index e071783..cfb4367 100644 --- a/src/modules/common/services/categories.service.ts +++ b/src/modules/common/services/categories.service.ts @@ -2,22 +2,18 @@ import { Component } from "@nestjs/common"; import { ObjectId } from "@models/common"; import { Model as CategoriesModel, cache } from "@models/Categroy"; import { isFunction } from "util"; +import { BaseService } from "./base"; interface IIdMap { [parentId: string]: ObjectId[]; } @Component() -export class CategoriesService { +export class CategoriesService extends BaseService { - private loadAndCache(FLAG: string, value: () => any, time?: number) { - const c = cache.get(FLAG); - if (c) { - return c; - } - const val = value(); - cache.put(FLAG, val, time); - return val; + constructor() { + super(); + super.setCache(cache); } private async getIdMap() { @@ -41,7 +37,7 @@ export class CategoriesService { } public async getChildrenIds(pid: ObjectId) { - const map: IIdMap = await this.loadAndCache("IdMap", () => { + const map = await this.loadAndCache("IdMap", () => { return this.getIdMap(); }); const ids: ObjectId[] = [ ]; diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index b742f28..8b1c51d 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -6,6 +6,7 @@ import { import { Model as CategroiesModel, ICategory } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; import { isUndefined } from "util"; +import { BaseService } from "./base"; export interface IGetRegexpsOptions { categroies?: ObjectId[]; @@ -13,9 +14,11 @@ export interface IGetRegexpsOptions { } @Component() -export class RegexpsService { +export class RegexpsService extends BaseService { constructor() { + super(); + super.setCache(cache); // Update setTimeout(() => { // Add Hidden Label @@ -26,13 +29,6 @@ export class RegexpsService { }, 3000); } - private loadAndCache(FLAG: string, value: any, time?: number | string) { - if (cache.get(FLAG) === null) { - cache.put(FLAG, value, time); - } - return cache.get(FLAG); - } - /** * 新增规则 */ @@ -96,21 +92,19 @@ export class RegexpsService { } } - public async pageCount(perNum = 1): Promise { + public pageCount(perNum = 1) { const FLAG = `pageCount_${perNum}`; return this.loadAndCache( FLAG, - Math.ceil((await this.count()) / perNum), - 3000 + async () => Math.ceil((await this.count()) / perNum) ); } - public count(): Promise { + public count() { const FLAG = "totalCount"; return this.loadAndCache( FLAG, - RegexpsModel.count({ }).exec(), - 3000 + () => RegexpsModel.count({ }).exec() ); } @@ -126,19 +120,18 @@ export class RegexpsService { * @param page {number} 页数 * @return {Promise} */ - public async list(perNum = DEF_PER_COUNT, page = 1): Promise { + public list(perNum = DEF_PER_COUNT, page = 1) { const FLAG = `list_${perNum}_${page}`; return this.loadAndCache( FLAG, - RegexpsModel.find({ }) + () => RegexpsModel.find({ }) .skip((page - 1) * perNum).limit(perNum) .populate("link").exec(), 3000 ); } - private async getRegexps(opts: IGetRegexpsOptions = { }) - : Promise { + private getRegexps(opts: IGetRegexpsOptions = { }) { const DEF_CONDITIONS = { link: { $exists: true }, hidden: false }; @@ -154,7 +147,7 @@ export class RegexpsService { }; return this.loadAndCache( FLAG, - RegexpsModel.find(conditions).populate("link").exec(), + () => RegexpsModel.find(conditions).populate("link").exec(), 3000 ); } else if (opts.appends && opts.appends.length > 0) { @@ -168,14 +161,14 @@ export class RegexpsService { }; return this.loadAndCache( FLAG, - RegexpsModel.find(conditions).populate("link").exec(), + () => RegexpsModel.find(conditions).populate("link").exec(), 3000 ); } else { const FLAG = "default_scan_regexps"; return this.loadAndCache( FLAG, - RegexpsModel.find(DEF_CONDITIONS).populate("link").exec(), + () => RegexpsModel.find(DEF_CONDITIONS).populate("link").exec(), 3000 ); } diff --git a/test/helpers/database/categories.ts b/test/helpers/database/categories.ts index 008203c..64c8a64 100644 --- a/test/helpers/database/categories.ts +++ b/test/helpers/database/categories.ts @@ -1,6 +1,6 @@ import { ObjectId } from "@models/common"; import { Model as CategoriesModel, CategoryDoc } from "@models/Categroy"; -import { newName } from "../utils"; +import { newName, sleep } from "../utils"; export const newCategory = (obj: object) => { return CategoriesModel.create(obj) as Promise; @@ -43,6 +43,7 @@ export const addCategories = async (pid?: ObjectId) => { await CategoriesModel.moveCategory(cids[9], pid); await CategoriesModel.moveCategory(cids[0], pid); } + await sleep(50); return cids; }; diff --git a/test/tsconfig.json b/test/tsconfig.json index 12f2f6b..a0ec75e 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -9,6 +9,7 @@ "mocha-steps", "should" ], + "strictFunctionTypes": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "importHelpers": true, diff --git a/tsconfig.json b/tsconfig.json index ecc45ee..1f8cf1b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "mongoose", "node" ], + "strictFunctionTypes": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "importHelpers": true, From a18268a3238553923ac550f4228082908be4bda2 Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 12 Mar 2018 20:01:07 +0800 Subject: [PATCH 22/49] Update Code Add - Common Service - Base Service - Goods Service Update - Format Category Info Goods List - Format Collection Info Goods List --- TODOLIST.md | 2 +- src/models/Collection.ts | 3 + .../categroies/categroies.controller.ts | 43 ++++----- .../collections.admin.controller.ts | 44 ++++----- .../collections/collections.controller.ts | 26 ++++-- src/modules/common/dtos/page.dto.ts | 4 +- .../services/{base.ts => base.service.ts} | 14 ++- .../common/services/categories.service.ts | 2 +- .../common/services/collections.service.ts | 81 +++++++++++------ src/modules/common/services/goods.service.ts | 20 ++++ .../common/services/regexps.service.ts | 14 +-- .../common/services/usergroups.service.ts | 25 +++-- src/modules/common/services/users.service.ts | 2 +- src/modules/common/services/util.service.ts | 44 +++++++++ src/modules/controllers.module.ts | 3 +- src/modules/files/goods.controller.ts | 14 +-- src/modules/goods/goods.controller.ts | 22 ++--- src/modules/regexps/regexps.controller.ts | 20 +--- src/modules/tokens/tokens.controller.ts | 8 +- .../usergroups/usergroups.controller.ts | 33 ++----- src/modules/users/users.controller.ts | 91 +++++-------------- test/api/collections.e2e.ts | 15 +-- test/services/collections.spec.ts | 8 +- 23 files changed, 285 insertions(+), 253 deletions(-) rename src/modules/common/services/{base.ts => base.service.ts} (63%) create mode 100644 src/modules/common/services/goods.service.ts create mode 100644 src/modules/common/services/util.service.ts diff --git a/TODOLIST.md b/TODOLIST.md index 92dbcf1..122df67 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -5,7 +5,7 @@ - [x] 默认用户组 - [x] 上传到指定Categroy - [x] 上传时追加Catogroy -- [ ] 整顿collectin info的goods 列表 +- [x] 整顿collectin info的goods 列表 - [ ] Token 使用日志显示 - [ ] Good 下载次数统计 - [ ] 接入统计 diff --git a/src/models/Collection.ts b/src/models/Collection.ts index c289a35..968830e 100644 --- a/src/models/Collection.ts +++ b/src/models/Collection.ts @@ -2,9 +2,12 @@ import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; import { Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS } from "@models/common"; import { IGoods, FLAG as GoodFlag, Model as GoodsModels } from "@models/Good"; import { IUser, FLAG as UserFlag } from "@models/User"; +import Cache = require("schedule-cache"); export const FLAG = "collections"; +export const cache = Cache.create(`${Date.now()}${Math.random()}`); + const Definition: SchemaDefinition = { name: { type: String, diff --git a/src/modules/categroies/categroies.controller.ts b/src/modules/categroies/categroies.controller.ts index 9bebac0..ebc21a7 100644 --- a/src/modules/categroies/categroies.controller.ts +++ b/src/modules/categroies/categroies.controller.ts @@ -9,12 +9,14 @@ import { Model as CategoriesModel, CategoryDoc, ICategory } from "@models/Categroy"; import { Model as ValuesModel, ValueDoc, IValues } from "@models/Value"; -import { Model as GoodsModels } from "@models/Good"; import { Roles } from "@decorators/roles"; import { RolesGuard } from "@guards/roles"; import { ParseIntPipe } from "@pipes/parse-int"; import { PerPageDto, ListResponse } from "@dtos/page"; import { CidDto } from "@dtos/ids"; +import { CategoriesService } from "@services/categories"; +import { GoodsService } from "@services/goods"; +import { UtilService } from "@services/util"; import md5 = require("md5"); import { @@ -30,6 +32,11 @@ import { CreateValueDto, EditValueDto } from "../values/values.dto"; // endregion Swagger Docs export class CategoriesAdminController { + constructor( + private readonly categoriesSvr: CategoriesService, + private readonly goodsSvr: GoodsService + ) { } + @Roles("admin") @Get() // region Swagger Docs @@ -41,18 +48,10 @@ export class CategoriesAdminController { }) // endregion Swagger Docs public async list(@Query(new ParseIntPipe()) query: PerPageDto) { - const curPage = query.page || 1; - const totalPages = await CategoriesModel.countCategories(query.perNum); - const totalCount = await CategoriesModel.countCategories(); - - const data = new ListResponse(); - data.current = curPage; - data.totalPages = totalPages; - data.total = totalCount; - if (totalPages >= curPage) { - data.data = await CategoriesModel.list(query.perNum, query.page); - } - return data; + const arr = await CategoriesModel.list(query.perNum, query.page); + return UtilService.toListRespone(arr, Object.assign({ + total: await CategoriesModel.countCategories() + }, query)); } @Roles("admin") @@ -111,7 +110,9 @@ export class CategoriesAdminController { @HttpCode(HttpStatus.OK) @ApiOperation({ title: "Get Category Info" }) // endregion Swagger Docs - public async get(@Param() param: CidDto) { + public async get( + @Param() param: CidDto, @Query(new ParseIntPipe()) query: PerPageDto + ) { let obj: ICategory; try { const doc = await CategoriesModel.findById(param.cid) @@ -124,15 +125,11 @@ export class CategoriesAdminController { } catch (error) { throw new BadRequestException(error.toString()); } - obj.goods = ( - await GoodsModels.find({ category: obj._id }) - .populate("uploader") - .populate("attributes") - .select("-category") - .exec() - ).map((doc) => { - return doc.toObject(); - }); + const arr = (await this.goodsSvr.listByCategoryId(param.cid)) + .map((doc) => { + return doc.toObject(); + }); + obj.goods = UtilService.toListRespone(arr, query); return obj; } diff --git a/src/modules/collections/collections.admin.controller.ts b/src/modules/collections/collections.admin.controller.ts index 438a53d..7901d8d 100644 --- a/src/modules/collections/collections.admin.controller.ts +++ b/src/modules/collections/collections.admin.controller.ts @@ -3,16 +3,18 @@ import { } from "@nestjs/swagger"; import { Controller, UseGuards, Get, HttpCode, HttpStatus, Session, Query, Post, - Body, Param, Delete + Body, Param, Delete, BadRequestException } from "@nestjs/common"; import { CollectionDoc } from "@models/Collection"; import { ObjectId } from "@models/common"; import { RolesGuard } from "@guards/roles"; import { CollectionsService } from "@services/collections"; +import { UtilService } from "@services/util"; import { Roles } from "@decorators/roles"; import { ParseIntPipe } from "@pipes/parse-int"; import { PerPageDto, DEF_PER_COUNT, ListResponse } from "@dtos/page"; import { CCidDto } from "@dtos/ids"; +import { DefResDto } from "@dtos/res"; import { CreateCollectionDto, EditCollectionDto, ICollection, IEditCollection } from "./collections.dto"; @@ -28,22 +30,13 @@ export class CollectionsAdminController { constructor(private readonly collectionsSvr: CollectionsService) { } private async getCollectionsRes(uid: ObjectId, query: PerPageDto) { - const curPage = query.page || 1; - const perNum = query.perNum || DEF_PER_COUNT; - const totalPages = - await this.collectionsSvr.countPage(uid, query.perNum); - const totalCount = await this.collectionsSvr.count(uid); - - const resData = new ListResponse(); - resData.current = curPage; - resData.totalPages = totalPages; - resData.total = totalCount; - if (totalPages >= curPage) { - resData.data = await this.collectionsSvr.list(uid, { - page: curPage, perNum - }); - } - return resData; + const arr = await this.collectionsSvr.list(uid, { + page: query.page, perNum: query.perNum + }); + const opts = Object.assign({ + total: await this.collectionsSvr.count(uid) + }, query); + return UtilService.toListRespone(arr, opts); } @Roles("admin") @@ -88,7 +81,7 @@ export class CollectionsAdminController { @HttpCode(HttpStatus.OK) @ApiOperation({ title: "Edit Collection" }) // endregion Swagger Docs - public editCollection( + public async editCollection( @Param() param: CCidDto, @Body() body: EditCollectionDto ) { const obj: IEditCollection = { }; @@ -98,7 +91,8 @@ export class CollectionsAdminController { if (body.goods) { obj.goods = body.goods; } - return this.collectionsSvr.edit(param.cid, obj); + await this.collectionsSvr.edit(param.cid, obj); + return new DefResDto(); } @Roles("admin") @@ -107,8 +101,16 @@ export class CollectionsAdminController { @HttpCode(HttpStatus.OK) @ApiOperation({ title: "Get One Collection's Info" }) // endregion Swagger Docs - public getCollection(@Param() param: CCidDto) { - return this.collectionsSvr.getById(param.cid); + public async getCollection(@Param() param: CCidDto) { + const doc = await this.collectionsSvr.getByCid(param.cid); + if (!doc) { + return null; + } + const obj = doc.toObject(); + obj.goods = UtilService.toListRespone(obj.goods as any[], { + perNum: obj.goods.length + }) as any; + return obj; } ///////////////////////// diff --git a/src/modules/collections/collections.controller.ts b/src/modules/collections/collections.controller.ts index 6b1097d..99c6e77 100644 --- a/src/modules/collections/collections.controller.ts +++ b/src/modules/collections/collections.controller.ts @@ -1,11 +1,13 @@ import { - UseGuards, Controller, Get, HttpCode, HttpStatus, Param + UseGuards, Controller, Get, HttpCode, HttpStatus, Param, Query } from "@nestjs/common"; import { ApiUseTags, ApiOperation } from "@nestjs/swagger"; import { CollectionsService } from "@services/collections"; +import { UtilService } from "@services/util"; import { RolesGuard } from "@guards/roles"; import { Roles } from "@decorators/roles"; import { GetCollectionNameDto } from "./collections.dto"; +import { PerPageDto } from "@dtos/page"; @UseGuards(RolesGuard) @Controller("/collections") @@ -22,12 +24,17 @@ export class CollectionsController { @HttpCode(HttpStatus.OK) @ApiOperation({ title: "Get Collection Info" }) // endregion Swagger Docs - public async getCollection(@Param() query: GetCollectionNameDto) { - const colDoc = await this.collectionsSvr.getByName(query.name); - let col; - col = colDoc.toObject(); - col.creator = col.creator.nickname; - col.goods.map((good) => { + public async getCollection( + @Param() param: GetCollectionNameDto, @Query() query: PerPageDto + ) { + const doc = await this.collectionsSvr.getByName(param.name); + if (!doc) { + return null; + } + let obj; + obj = doc.toObject(); + obj.creator = obj.creator.nickname; + obj.goods.map((good) => { const keys = [ "__v", "uploader", "hidden" ]; @@ -36,7 +43,10 @@ export class CollectionsController { } return good; }); - return col; + obj.goods = UtilService.toListRespone(obj.goods, { + perNum: obj.goods.length + }); + return obj; } } diff --git a/src/modules/common/dtos/page.dto.ts b/src/modules/common/dtos/page.dto.ts index 2fc97b6..86ebf66 100644 --- a/src/modules/common/dtos/page.dto.ts +++ b/src/modules/common/dtos/page.dto.ts @@ -23,8 +23,8 @@ export class ListResponse { } export interface IPerPage { - readonly perNum: number; - readonly page: number; + readonly perNum?: number; + readonly page?: number; } export class PerPageDto implements IPerPage { diff --git a/src/modules/common/services/base.ts b/src/modules/common/services/base.service.ts similarity index 63% rename from src/modules/common/services/base.ts rename to src/modules/common/services/base.service.ts index 1362a44..91c70b9 100644 --- a/src/modules/common/services/base.ts +++ b/src/modules/common/services/base.service.ts @@ -1,3 +1,6 @@ +import { DEF_PER_COUNT } from "@dtos/page"; +import { UtilService } from "@services/util"; + type TimeType = number | string; export abstract class BaseService { @@ -5,7 +8,7 @@ export abstract class BaseService { private cache; protected setCache(cache) { - this.cache = cache; + // this.cache = cache; } protected loadAndCache( @@ -27,4 +30,13 @@ export abstract class BaseService { return val; } + protected DEF_PER_OBJ = UtilService.DEF_PER_OBJ; + + /** + * 计算页数 + * @param total 总数 + * @param perNum 每页显示数 + */ + public calPageCount = UtilService.calPageCount; + } diff --git a/src/modules/common/services/categories.service.ts b/src/modules/common/services/categories.service.ts index cfb4367..8bc9a26 100644 --- a/src/modules/common/services/categories.service.ts +++ b/src/modules/common/services/categories.service.ts @@ -2,7 +2,7 @@ import { Component } from "@nestjs/common"; import { ObjectId } from "@models/common"; import { Model as CategoriesModel, cache } from "@models/Categroy"; import { isFunction } from "util"; -import { BaseService } from "./base"; +import { BaseService } from "@services/base"; interface IIdMap { [parentId: string]: ObjectId[]; diff --git a/src/modules/common/services/collections.service.ts b/src/modules/common/services/collections.service.ts index d66ce7f..5ade3c5 100644 --- a/src/modules/common/services/collections.service.ts +++ b/src/modules/common/services/collections.service.ts @@ -1,17 +1,18 @@ import { Component, Param, BadRequestException } from "@nestjs/common"; import { UidDto } from "@dtos/ids"; -import { Model as CollectionsModel } from "@models/Collection"; +import { Model as CollectionsModel, cache } from "@models/Collection"; import { ObjectId } from "@models/common"; -import { IPerPage, DEF_PER_COUNT } from "@dtos/page"; +import { DEF_PER_COUNT } from "@dtos/page"; import { IEditCollection } from "../../../modules/collections/collections.dto"; +import { BaseService } from "@services/base"; @Component() -export class CollectionsService { +export class CollectionsService extends BaseService { - private DEF_PER_OBJ: IPerPage = { - perNum: DEF_PER_COUNT, - page: 1 - }; + constructor() { + super(); + this.setCache(cache); + } public async create(obj) { try { @@ -31,38 +32,60 @@ export class CollectionsService { } } - public list(uid: ObjectId, pageObj: IPerPage = this.DEF_PER_OBJ) { + public list(uid: ObjectId, pageObj = this.DEF_PER_OBJ) { const perNum = pageObj.perNum; const page = pageObj.page; - return CollectionsModel.find({ creator: uid }) - .skip((page - 1) * perNum).limit(perNum) - .sort({ updatedAt: -1 }) - .populate("creator") - .populate("goods") - .exec(); + return this.loadAndCache( + `list_${uid.toString}_${perNum}_${page}`, + () => { + return CollectionsModel.find({ creator: uid }) + .skip((page - 1) * perNum).limit(perNum) + .sort({ updatedAt: -1 }) + .populate("creator") + .populate("goods") + .exec(); + }, + 7200 + ); } public count(uid: ObjectId) { - return CollectionsModel.count({ creator: uid }).exec(); + return this.loadAndCache( + `count_${uid}`, + () => CollectionsModel.count({ creator: uid }).exec() + ); } - public async countPage(uid: ObjectId, perNum = DEF_PER_COUNT) { - const total = await this.count(uid); - return Math.ceil(total / perNum); + public getByName(name: string, opts = this.GetOptions) { + let p = CollectionsModel.findOne({ name }); + if (opts.populate && opts.populate.length > 0) { + for (const field of opts.populate) { + p = p.populate(field); + } + } + return this.loadAndCache( + `getByName_${name}`, + () => p.exec(), + 7200 + ); } - public getByName(name: string) { - return CollectionsModel.findOne({ name }) - .populate("creator") - .populate("goods") - .exec(); - } + private readonly GetOptions = { + populate: [ "creator", "goods" ] + }; - public getById(cid: ObjectId) { - return CollectionsModel.findById(cid) - .populate("creator") - .populate("goods") - .exec(); + public getByCid(cid: ObjectId, opts = this.GetOptions) { + let p = CollectionsModel.findById(cid); + if (opts.populate && opts.populate.length > 0) { + for (const field of opts.populate) { + p = p.populate(field); + } + } + return this.loadAndCache( + `getById_${cid.toString()}`, + () => p.exec(), + 7200 + ); } public async remove(cid: ObjectId) { diff --git a/src/modules/common/services/goods.service.ts b/src/modules/common/services/goods.service.ts new file mode 100644 index 0000000..37d5bd5 --- /dev/null +++ b/src/modules/common/services/goods.service.ts @@ -0,0 +1,20 @@ +import { Component } from "@nestjs/common"; +import { Model as GoodsModels } from "@models/Good"; +import { ObjectId } from "@models/common"; +import { BaseService } from "@services/base"; + +@Component() +export class GoodsService extends BaseService { + + public listByCategoryId(cid: ObjectId, pageObj = this.DEF_PER_OBJ) { + return this.loadAndCache( + `list_category_${cid.toString()}`, + () => GoodsModels.find({ category: cid }) + .populate("uploader") + .populate("attributes") + .select("-category") + .exec() + ); + } + +} diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index 8b1c51d..ed585bc 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -6,7 +6,7 @@ import { import { Model as CategroiesModel, ICategory } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; import { isUndefined } from "util"; -import { BaseService } from "./base"; +import { BaseService } from "@services/base"; export interface IGetRegexpsOptions { categroies?: ObjectId[]; @@ -92,11 +92,9 @@ export class RegexpsService extends BaseService { } } - public pageCount(perNum = 1) { - const FLAG = `pageCount_${perNum}`; - return this.loadAndCache( - FLAG, - async () => Math.ceil((await this.count()) / perNum) + public async pageCount(perNum?: number) { + return this.calPageCount( + await this.count(), perNum ); } @@ -120,7 +118,9 @@ export class RegexpsService extends BaseService { * @param page {number} 页数 * @return {Promise} */ - public list(perNum = DEF_PER_COUNT, page = 1) { + public list( + perNum = this.DEF_PER_OBJ.perNum, page = this.DEF_PER_OBJ.page + ) { const FLAG = `list_${perNum}_${page}`; return this.loadAndCache( FLAG, diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index 98f5cdb..bb50136 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -3,15 +3,10 @@ import { Model as UsersModel } from "@models/User"; import { Model as UsergroupsModel } from "@models/Usergroup"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { ObjectId } from "@models/common"; -import { DEF_PER_COUNT, IPerPage } from "@dtos/page"; +import { BaseService } from "@services/base"; @Component() -export class UsergroupsService { - - private DEF_PER_OBJ: IPerPage = { - perNum: DEF_PER_COUNT, - page: 1 - }; +export class UsergroupsService extends BaseService { public async add(obj: object) { try { @@ -35,8 +30,10 @@ export class UsergroupsService { return UserUsergroupsModel.count({ usergroup: gid }).exec(); } - public async usersCountPage(id: ObjectId, perNum = DEF_PER_COUNT) { - const total = await this.usersCount(id); + public async usersCountPage( + uid: ObjectId, perNum = this.DEF_PER_OBJ.perNum + ) { + const total = await this.usersCount(uid); return Math.ceil(total / perNum); } @@ -45,7 +42,7 @@ export class UsergroupsService { } public async getGroupUsers( - gid: ObjectId, pageObj: IPerPage = this.DEF_PER_OBJ + gid: ObjectId, pageObj = this.DEF_PER_OBJ ) { const perNum = pageObj.perNum; const page = pageObj.page; @@ -61,14 +58,14 @@ export class UsergroupsService { return UsergroupsModel.count({ }).exec(); } - public async countPage(perNum = DEF_PER_COUNT) { + public async countPage(perNum = this.DEF_PER_OBJ.perNum) { const total = await this.count(); return Math.ceil(total / perNum); } - public list(pageObj: IPerPage = this.DEF_PER_OBJ) { - const perNum = pageObj.perNum; - const page = pageObj.page; + public list(pageObj = this.DEF_PER_OBJ) { + const perNum = pageObj.perNum || this.DEF_PER_OBJ.perNum; + const page = pageObj.page || this.DEF_PER_OBJ.page; return UsergroupsModel.find({ }) .skip((page - 1) * perNum).limit(perNum) .sort({ createdAt: -1 }) diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index 27bd62a..7c1961b 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -50,7 +50,7 @@ export class UsersService { } public async getUsergroups( - uid: ObjectId, pageObj: IPerPage = this.DEF_PER_OBJ + uid: ObjectId, pageObj = this.DEF_PER_OBJ ) { const perNum = pageObj.perNum; const page = pageObj.page; diff --git a/src/modules/common/services/util.service.ts b/src/modules/common/services/util.service.ts new file mode 100644 index 0000000..552eb47 --- /dev/null +++ b/src/modules/common/services/util.service.ts @@ -0,0 +1,44 @@ +import { IPerPage, DEF_PER_COUNT, ListResponse } from "@dtos/page"; + +interface IToListResponeOptions extends IPerPage { + total?: number; +} + +export abstract class UtilService { + + public static DEF_PER_OBJ: IPerPage = { + perNum: DEF_PER_COUNT, + page: 1 + }; + + /** + * 计算页数 + * @param total 总数 + * @param perNum 每页显示数 + */ + public static calPageCount( + total: number, perNum = UtilService.DEF_PER_OBJ.perNum + ) { + return Math.ceil(total / perNum); + } + + public static toListRespone( + arr: T[], opts: IToListResponeOptions = UtilService.DEF_PER_OBJ + ) { + const perNum = opts.perNum || UtilService.DEF_PER_OBJ.perNum; + const listRes = new ListResponse(); + listRes.total = opts.total || arr.length; + listRes.current = opts.page || UtilService.DEF_PER_OBJ.page; + listRes.totalPages = + UtilService.calPageCount(listRes.total, perNum); + + if (opts.total && listRes.totalPages >= listRes.current) { + listRes.data = arr; + } else { + const startIndex = (listRes.current - 1) * perNum; + listRes.data = arr.slice(startIndex, startIndex + perNum); + } + return listRes; + } + +} diff --git a/src/modules/controllers.module.ts b/src/modules/controllers.module.ts index 5914375..b383edd 100644 --- a/src/modules/controllers.module.ts +++ b/src/modules/controllers.module.ts @@ -38,6 +38,7 @@ import { TokensService } from "@services/tokens"; import { UsergroupsService } from "@services/usergroups"; import { SystemService } from "@services/system"; import { CategoriesService } from "@services/categories"; +import { GoodsService } from "@services/goods"; // endregion Services export const controllers = [ @@ -51,7 +52,7 @@ export const controllers = [ ]; export const services = [ - RegexpsService, CategoriesService, + RegexpsService, CategoriesService, GoodsService, CollectionsService, TokensService, UsersService, UsergroupsService, SystemService ]; diff --git a/src/modules/files/goods.controller.ts b/src/modules/files/goods.controller.ts index 9f19ea0..7917d0b 100644 --- a/src/modules/files/goods.controller.ts +++ b/src/modules/files/goods.controller.ts @@ -1,9 +1,10 @@ import { UseGuards, Controller, Get, HttpCode, HttpStatus, Query } from "@nestjs/common"; -import { ApiUseTags, ApiOperation } from "@nestjs/swagger"; +import { ApiUseTags, ApiOperation, ApiResponse } from "@nestjs/swagger"; import { Model as CategoriesModel } from "@models/Categroy"; import { Model as GoodsModels } from "@models/Good"; +import { UtilService } from "@services/util"; import { IUser } from "@models/User"; import { IGoodsRaw } from "@models/Good"; import { RolesGuard } from "@guards/roles"; @@ -24,16 +25,16 @@ export class GoodsController { // region Swagger Docs @HttpCode(HttpStatus.OK) @ApiOperation({ title: "Get Good List" }) + @ApiResponse({ status: HttpStatus.OK, type: ListResponse }) // endregion Swagger Docs public async getList(@Query(new ParseIntPipe()) query: GoodsQueryDto) { - const data = new ListResponse(); const categoryModels = await CategoriesModel.getCategories(query.tags); const categories = reduce(categoryModels, (obj, cate) => { obj[cate._id.toString()] = cate; return obj; }, { }); if (Object.keys(categories).length === 0) { - return data; + return UtilService.toListRespone([ ]); } const perNum = query.perNum || DEF_PER_COUNT; @@ -52,9 +53,8 @@ export class GoodsController { )) as any; return good; }); - data.data = goods; - data.totalPages = await GoodsModels.countGoodsByCids(cids, perNum); - data.total = await GoodsModels.countGoodsByCids(cids); - return data; + return UtilService.toListRespone(goods, Object.assign({ + total: await GoodsModels.countGoodsByCids(cids) + }, query)); } } diff --git a/src/modules/goods/goods.controller.ts b/src/modules/goods/goods.controller.ts index fc6779f..bf663fe 100644 --- a/src/modules/goods/goods.controller.ts +++ b/src/modules/goods/goods.controller.ts @@ -27,6 +27,7 @@ import { TokensService } from "@services/tokens"; import { CollectionsService } from "@services/collections"; import { IGetRegexpsOptions, RegexpsService } from "@services/regexps"; import { CategoriesService } from "@services/categories"; +import { UtilService } from "@services/util"; import * as hasha from "hasha"; import fs = require("fs-extra"); import multer = require("multer"); @@ -68,21 +69,12 @@ export class GoodsAdminController { }) // endregion Swagger Docs public async getGoods(@Query(new ParseIntPipe()) query: PerPageDto) { - const curPage = query.page || 1; - const totalPages = - await GoodsModels.countGoodsByUids([ ], query.perNum); - const totalCount = await GoodsModels.countGoodsByUids([ ]); - - const resData = new ListResponse(); - resData.current = curPage; - resData.totalPages = totalPages; - resData.total = totalCount; - if (totalPages >= curPage) { - resData.data = await GoodsModels.getGoodsByUids( - [ ], query.perNum, query.page - ); - } - return resData; + const arr = await GoodsModels.getGoodsByUids( + [ ], query.perNum, query.page + ); + return UtilService.toListRespone(arr, Object.assign({ + total: await GoodsModels.countGoodsByUids([ ]) + }, query)); } private async getCategoriesIds(names: string[]) { diff --git a/src/modules/regexps/regexps.controller.ts b/src/modules/regexps/regexps.controller.ts index 4eda6ef..30af327 100644 --- a/src/modules/regexps/regexps.controller.ts +++ b/src/modules/regexps/regexps.controller.ts @@ -15,6 +15,7 @@ import { PerPageDto, ListResponse } from "@dtos/page"; import { RidDto } from "@dtos/ids"; import { ParseIntPipe } from "@pipes/parse-int"; import { RegexpsService } from "@services/regexps"; +import { UtilService } from "@services/util"; @UseGuards(RolesGuard) @Controller("api/v1/regexps") @@ -39,21 +40,10 @@ export class RegexpsAdminController { }) // endregion Swagger Docs public async list(@Query(new ParseIntPipe()) query: PerPageDto) { - const curPage = query.page || 1; - const totalPages = await this.regexpsSvr.pageCount(query.perNum); - const totalCount = await this.regexpsSvr.count(); - - const data = new ListResponse(); - data.current = curPage; - data.totalPages = totalPages; - data.total = totalCount; - if (data.current > data.totalPages) { - data.current = data.totalPages; - } - if (totalPages >= curPage) { - data.data = await this.regexpsSvr.list(query.perNum, data.current); - } - return data; + const arr = await this.regexpsSvr.list(query.perNum, query.page); + return UtilService.toListRespone(arr, Object.assign({ + total: await this.regexpsSvr.count() + }, query)); } @Roles("admin") diff --git a/src/modules/tokens/tokens.controller.ts b/src/modules/tokens/tokens.controller.ts index deae07f..383b167 100644 --- a/src/modules/tokens/tokens.controller.ts +++ b/src/modules/tokens/tokens.controller.ts @@ -7,6 +7,7 @@ import { Model as TokensModel } from "@models/Token"; import { Roles } from "@decorators/roles"; import { RolesGuard } from "@guards/roles"; import { TokensService } from "@services/tokens"; +import { UtilService } from "@services/util"; import { DefResDto } from "@dtos/res"; import { ListResponse } from "@dtos/page"; import { TokenParamDto } from "./tokens.dto"; @@ -29,11 +30,8 @@ export class TokensAdminController { }) // endregion Swagger Docs public async getTokens(@Session() session) { - const data = new ListResponse(); - data.current = data.totalPages = 1; - data.data = await this.tokensSvr.getTokens(session.loginUserId); - data.total = data.data.length; - return data; + const arr = await this.tokensSvr.getTokens(session.loginUserId); + return UtilService.toListRespone(arr); } @Roles("admin") diff --git a/src/modules/usergroups/usergroups.controller.ts b/src/modules/usergroups/usergroups.controller.ts index 187cfac..d071d82 100644 --- a/src/modules/usergroups/usergroups.controller.ts +++ b/src/modules/usergroups/usergroups.controller.ts @@ -6,6 +6,7 @@ import { Roles } from "@decorators/roles"; import { ParseIntPipe } from "@pipes/parse-int"; import { PerPageDto, ListResponse, DEF_PER_COUNT } from "@dtos/page"; import { UGidDto } from "@dtos/ids"; +import { UtilService } from "@services/util"; import { AddUsergroupDto, EditUsergroupDto, UserUsergroupDto } from "./usergroups.dto"; @@ -27,18 +28,10 @@ export class UsergroupsAdminController { }) // endregion Swagger Docs public async getList(@Query(new ParseIntPipe()) query: PerPageDto) { - const curPage = query.page || 1; - const perNum = query.perNum || DEF_PER_COUNT; - const resData = new ListResponse(); - resData.current = curPage; - resData.totalPages = await this.ugSvr.countPage(perNum); - resData.total = await this.ugSvr.count(); - if (resData.totalPages >= resData.current) { - resData.data = await this.ugSvr.list({ - page: curPage, perNum - }); - } - return resData; + const arr = await this.ugSvr.list(query); + return UtilService.toListRespone(arr, Object.assign({ + total: await this.ugSvr.count() + }, query)); } @Roles("admin") @@ -112,18 +105,10 @@ export class UsergroupsAdminController { return group; } group = group.toObject(); - const users = new ListResponse(); - const curPage = query.page || 1; - const perNum = query.perNum || DEF_PER_COUNT; - users.current = curPage; - users.totalPages = await this.ugSvr.usersCountPage(param.gid, perNum); - users.total = await this.ugSvr.usersCount(param.gid); - if (users.totalPages >= users.current) { - users.data = await this.ugSvr.getGroupUsers(param.gid, { - page: curPage, perNum - }); - } - group.users = users; + const arr = await this.ugSvr.getGroupUsers(param.gid, query); + group.users = UtilService.toListRespone(arr, Object.assign({ + total: await this.ugSvr.usersCount(param.gid) + }, query)); return group; } diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 2f61db4..f21e480 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -19,6 +19,7 @@ import { UsergroupsService } from "@services/usergroups"; import { CollectionsService } from "@services/collections"; import { UsersService } from "@services/users"; import { SystemService } from "@services/system"; +import { UtilService } from "@services/util"; import { PerPageDto, ListResponse, DEF_PER_COUNT } from "@dtos/page"; import { UidDto } from "@dtos/ids"; import { DefResDto } from "@dtos/res"; @@ -54,18 +55,10 @@ export class UsersAdminController { }) // endregion Swagger Docs public async findAll(@Query(new ParseIntPipe()) query: PerPageDto) { - const curPage = query.page || 1; - const totalPages = await UserModel.countUsers(query.perNum); - const totalCount = await UserModel.countUsers(); - - const data = new ListResponse(); - data.current = curPage; - data.totalPages = totalPages; - data.total = totalCount; - if (totalPages >= curPage) { - data.data = await UserModel.list(query.perNum, query.page); - } - return data; + const arr = await UserModel.list(query.perNum, query.page); + return UtilService.toListRespone(arr, Object.assign({ + total: await UserModel.countUsers() + }, query)); } @Roles("admin") @@ -208,11 +201,8 @@ export class UsersAdminController { }) // endregion Swagger Docs public async getSelfTokens(@Session() session) { - const data = new ListResponse(); - data.current = data.totalPages = 1; - data.data = await this.tokensSvr.getTokens(session.loginUserId); - data.total = data.data.length; - return data; + const arr = await this.tokensSvr.getTokens(session.loginUserId); + return UtilService.toListRespone(arr); } @Roles("admin") @@ -226,11 +216,8 @@ export class UsersAdminController { }) // endregion Swagger Docs public async getTokens(@Param() param: UidDto, @Session() session) { - const data = new ListResponse(); - data.current = data.totalPages = 1; - data.data = await this.tokensSvr.getTokens(param.uid); - data.total = data.data.length; - return data; + const arr = await this.tokensSvr.getTokens(param.uid); + return UtilService.toListRespone(arr); } //////////////////////////////////////// @@ -242,22 +229,12 @@ export class UsersAdminController { //////////////////////////////////////// private async getGoodsRes(uid: ObjectId, query: PerPageDto) { - const curPage = query.page || 1; - const perNum = query.perNum || DEF_PER_COUNT; - const totalPages = - await GoodsModels.countGoodsByUids(uid, query.perNum); - const totalCount = await GoodsModels.countGoodsByUids(uid); - - const resData = new ListResponse(); - resData.current = curPage; - resData.totalPages = totalPages; - resData.total = totalCount; - if (totalPages >= curPage) { - resData.data = await GoodsModels.getGoodsByUids( - uid, query.perNum, query.page - ); - } - return resData; + const arr = await GoodsModels.getGoodsByUids( + uid, query.perNum, query.page + ); + return UtilService.toListRespone(arr, Object.assign({ + total: await GoodsModels.countGoodsByUids(uid) + }, query)); } @Roles("admin", "token") @@ -303,20 +280,10 @@ export class UsersAdminController { //////////////////////////////////////// private async getCollectionsRes(uid: ObjectId, query: PerPageDto) { - const curPage = query.page || 1; - const perNum = query.perNum || DEF_PER_COUNT; - const totalPages = - await this.collectionsSvr.countPage(uid, query.perNum); - const totalCount = await this.collectionsSvr.count(uid); - - const resData = new ListResponse(); - resData.current = curPage; - resData.totalPages = totalPages; - resData.total = totalCount; - if (totalPages >= curPage) { - resData.data = await this.collectionsSvr.list(uid, query); - } - return resData; + const arr = await this.collectionsSvr.list(uid, query); + return UtilService.toListRespone(arr, { + total: await this.collectionsSvr.count(uid) + }); } @Roles("admin", "token") @@ -373,22 +340,10 @@ export class UsersAdminController { public async getUsergroups( @Param() param: UidDto, @Query(new ParseIntPipe()) query: PerPageDto ) { - const curPage = query.page || 1; - const perNum = query.perNum || DEF_PER_COUNT; - const totalPages = - await this.usersSvr.countPageUsergroups(param.uid, query.perNum); - const totalCount = await this.usersSvr.countUsergroups(param.uid); - - const resData = new ListResponse(); - resData.current = curPage; - resData.totalPages = totalPages; - resData.total = totalCount; - if (totalPages >= curPage) { - resData.data = await this.usersSvr.getUsergroups(param.uid, { - page: curPage, perNum - }); - } - return resData; + const arr = await this.usersSvr.getUsergroups(param.uid, query); + return UtilService.toListRespone(arr, Object.assign({ + total: await this.usersSvr.countUsergroups(param.uid) + }, query)); } //////////////////////////////////////// diff --git a/test/api/collections.e2e.ts b/test/api/collections.e2e.ts index cee0ef8..13db734 100644 --- a/test/api/collections.e2e.ts +++ b/test/api/collections.e2e.ts @@ -55,7 +55,7 @@ describe("Collections E2E Api", () => { return files.remove(filepaths); }); - before("login", async () => { + step("login", async () => { ids.users.push((await auth.login(request))[0]); }); @@ -81,9 +81,11 @@ describe("Collections E2E Api", () => { }); step("User's Collection List", async () => { + const userId = ids.users[ids.users.length - 1]; + const url = `/api/v1/users/${userId}/collections`; const { body: result, status - } = await request.get("/api/v1/users/collections").then(); + } = await request.get(url).then(); status.should.be.eql(200); result.should.have.properties("total", "data"); result.data.should.be.an.Array() @@ -104,9 +106,10 @@ describe("Collections E2E Api", () => { step("Other User's Collection List", async () => { const userId = ids.users[ids.users.length - 1]; + const url = `/api/v1/users/${userId}/collections`; const { body: result, status - } = await request.get(`/api/v1/users/${userId}/collections`).then(); + } = await request.get(url).then(); status.should.be.eql(200); result.should.have.properties("total", "data"); result.data.should.be.an.Array() @@ -137,8 +140,8 @@ describe("Collections E2E Api", () => { } = await request.get(`/api/v1/collections/${collectionId}`).then(); status.should.be.eql(200); result.should.have.properties("name", "goods", "creator"); - result.goods.should.be.an.Array().which.have.length(FILE_COUNST); - goods = result.goods; + result.goods.data.should.be.an.Array().which.have.length(FILE_COUNST); + goods = result.goods.data; }); step("Add New Collection", async () => { @@ -277,7 +280,7 @@ describe("Collections E2E Api", () => { body: result } = await request.get(`/api/v1/collections/${id}`) .then(); - result.goods.should.have.length(4); + result.goods.data.should.have.length(4); }); step("Delete by DELETE METHOD", async () => { diff --git a/test/services/collections.spec.ts b/test/services/collections.spec.ts index d9efd61..f6e4478 100644 --- a/test/services/collections.spec.ts +++ b/test/services/collections.spec.ts @@ -59,10 +59,10 @@ describe("Collections Service Test Unit", () => { should(count).be.a.Number(); }); - it("The Function `countPage` will return number", async () => { - const user = await UsersModel.findOne().exec(); - const count = await collectionsSvr.countPage(user._id); - should(count).be.a.Number(); + it.skip("The Function `countPage` will return number", async () => { + // const user = await UsersModel.findOne().exec(); + // const count = await collectionsSvr.countPage(user._id); + // should(count).be.a.Number(); }); it("The Function `list` will return array", async () => { From 6e96c0c8951f9e6141cf105c362eab0395fb3750 Mon Sep 17 00:00:00 2001 From: Arylo Date: Tue, 13 Mar 2018 10:28:14 +0800 Subject: [PATCH 23/49] Remove Unset function --- src/modules/common/services/regexps.service.ts | 6 ------ .../common/services/usergroups.service.ts | 7 ------- src/modules/common/services/users.service.ts | 18 +++++------------- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index ed585bc..a665970 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -92,12 +92,6 @@ export class RegexpsService extends BaseService { } } - public async pageCount(perNum?: number) { - return this.calPageCount( - await this.count(), perNum - ); - } - public count() { const FLAG = "totalCount"; return this.loadAndCache( diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index bb50136..beeecf8 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -30,13 +30,6 @@ export class UsergroupsService extends BaseService { return UserUsergroupsModel.count({ usergroup: gid }).exec(); } - public async usersCountPage( - uid: ObjectId, perNum = this.DEF_PER_OBJ.perNum - ) { - const total = await this.usersCount(uid); - return Math.ceil(total / perNum); - } - public getGroup(gid: ObjectId) { return UsergroupsModel.findById(gid).exec(); } diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index 7c1961b..d06a483 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -4,17 +4,14 @@ import { Model as UsersModel, UserDoc } from "@models/User"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { IUsergroups } from "@models/Usergroup"; import { SystemService } from "@services/system"; -import { IPerPage, DEF_PER_COUNT } from "@dtos/page"; +import { BaseService } from "@services/base"; @Component() -export class UsersService { +export class UsersService extends BaseService { - private DEF_PER_OBJ: IPerPage = { - perNum: DEF_PER_COUNT, - page: 1 - }; - - constructor(private readonly sysSvr: SystemService) { } + constructor(private readonly sysSvr: SystemService) { + super(); + } public async addUser(obj, gid?: ObjectId) { try { @@ -44,11 +41,6 @@ export class UsersService { return UserUsergroupsModel.count({ user: uid }).exec(); } - public async countPageUsergroups(uid: ObjectId, perNum = DEF_PER_COUNT) { - const total = await this.countUsergroups(uid); - return Math.ceil(total / perNum); - } - public async getUsergroups( uid: ObjectId, pageObj = this.DEF_PER_OBJ ) { From 949fece78af5d93bcb02a4a3fa541ef6fd9596ec Mon Sep 17 00:00:00 2001 From: Arylo Date: Tue, 13 Mar 2018 17:13:35 +0800 Subject: [PATCH 24/49] Update Code - Remove Models from controllers - Update Serivecs - Move any model static function to service --- src/models/Categroy.ts | 75 ---------- src/models/Good.ts | 128 +---------------- src/models/Token.ts | 9 +- src/models/User.ts | 26 ---- .../categroies/categroies.controller.ts | 112 +++++++-------- .../collections.admin.controller.ts | 2 +- .../collections/collections.controller.ts | 4 +- src/modules/common/services/base.service.ts | 19 +++ .../common/services/categories.service.ts | 100 ++++++++++++- .../common/services/collections.service.ts | 38 ++--- src/modules/common/services/goods.service.ts | 131 +++++++++++++++++- .../common/services/regexps.service.ts | 14 +- src/modules/common/services/tokens.service.ts | 16 ++- .../common/services/usergroups.service.ts | 17 ++- src/modules/common/services/users.service.ts | 55 +++++++- src/modules/files/files.controller.ts | 11 +- src/modules/files/goods.controller.ts | 15 +- src/modules/goods/goods.controller.ts | 70 +++++----- src/modules/regexps/regexps.controller.ts | 17 ++- src/modules/tokens/tokens.controller.ts | 17 +-- .../usergroups/usergroups.controller.ts | 9 +- src/modules/users/auth.controller.ts | 26 ++-- src/modules/users/users.controller.ts | 29 ++-- test/models/user.spec.ts | 10 +- 24 files changed, 508 insertions(+), 442 deletions(-) diff --git a/src/models/Categroy.ts b/src/models/Categroy.ts index bf46d10..6e1797b 100644 --- a/src/models/Categroy.ts +++ b/src/models/Categroy.ts @@ -39,28 +39,6 @@ export interface ICategoryRaw extends ICategory { const CategorySchema = new Base(Definition).createSchema(); -CategorySchema.static("countCategories", async (perNum = 1) => { - const FLAG = `page_count_${perNum}`; - if (cache.get(FLAG)) { - return cache.get(FLAG); - } - const count = Math.ceil((await Model.count({ }).exec()) / perNum); - cache.put(FLAG, count); - return cache.get(FLAG); -}); - -CategorySchema.static("list", (perNum = DEF_PER_COUNT, page = 1) => { - const FLAG_LIST = `list_${perNum}_${page}`; - if (cache.get(FLAG_LIST)) { - return cache.get(FLAG_LIST); - } - const p = Model.find({ }) - .skip((page - 1) * perNum).limit(perNum) - .exec(); - cache.put(FLAG_LIST, p); - return cache.get(FLAG_LIST); -}); - const getIdGroups = (obj): string[] => { const selfIdArr = [ obj._id.toString() ]; if (obj.pid) { @@ -101,52 +79,8 @@ CategorySchema.static("moveCategory", async (id: ObjectId, pid: ObjectId) => { }).exec(); }); -CategorySchema.static("getCategories", async (tags: string | string[] = [ ]) => { - if (!isArray(tags)) { - tags = [ tags ]; - } - if (tags.length === 0) { - return Promise.resolve([ ]); - } - const conditions = tags.length === 1 ? { - tags: { $in: tags } - } : { - $or: reduce(tags, (arr, tag) => { - arr.push({ tags: { $in: [ tag ] } }); - return arr; - }, []) - }; - const p = (await Model.find(conditions) - .populate({ path: "pid", populate: { path: "pid" } }) - .populate("attributes") - .exec()) - .map((item) => item.toObject()) - .map((item) => { - item.tags = Array.from(new Set(getTags(item))); - delete item.pid; - return item; - }) - .filter((item) => { - const diffLength = difference(item.tags, tags).length; - return diffLength + tags.length === item.tags.length ; - }); - return p; -}); - export interface ICategoryModel extends M { moveCategory(id: ObjectId, pid: ObjectId): Promise; - getCategories(tags: string | string[]): Promise; - /** - * Category 列表 - * @param perNum {number} 每页数量 - * @param page {number} 页数 - * @return {Promise} - */ - list(perNum?: number, page?: number): Promise; - /** - * 返回总页数 - */ - countCategories(perNum?: number): Promise; } for (const method of MODIFY_MOTHODS) { @@ -156,12 +90,3 @@ for (const method of MODIFY_MOTHODS) { } export const Model = model(FLAG, CategorySchema) as ICategoryModel; - -const getTags = (obj: ICategoryRaw | ICategory) => { - const tags = obj.tags; - const pid = obj.pid as ICategoryRaw | void; - if (pid && pid.tags) { - return tags.concat(getTags(pid)); - } - return tags; -}; diff --git a/src/models/Good.ts b/src/models/Good.ts index 0ba2214..1b32663 100644 --- a/src/models/Good.ts +++ b/src/models/Good.ts @@ -3,10 +3,7 @@ import { Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS } from "@models/common"; import { IValues, Flag as ValueFlag } from "@models/Value"; import { IUser, FLAG as UserFlag } from "@models/User"; import { ICategory, FLAG as CategoryFlag } from "@models/Categroy"; -import { DEF_PER_COUNT } from "@dtos/page"; import Cache = require("schedule-cache"); -import { isArray } from "util"; -import { reduce } from "lodash"; const cache = Cache.create(`${Date.now()}${Math.random()}`); @@ -83,133 +80,10 @@ GoodsSchema.path("sha256sum").validate({ }); // endregion validators -const getConditionsByUids = (uids: ObjectId[]) => { - let conditions; - switch (uids.length) { - case 0: - conditions = { }; - break; - case 1: - conditions = { - uploader: uids[0] - }; - break; - default: - conditions = { - $or: reduce(uids, (arr, uid) => { - arr.push({ uploader: uid }); - return arr; - }, []) - }; - break; - } - return conditions; -}; - -GoodsSchema.static( - "getGoodsByUids", - (uids: ObjectId | ObjectId[], perNum = DEF_PER_COUNT, page = 1) => { - if (!isArray(uids)) { - uids = [ uids ]; - } - const conditions = getConditionsByUids(uids); - return Model.find(conditions) - .skip((page - 1) * perNum).limit(perNum) - .select("-uploader") - .populate("attributes") - .sort({ updatedAt: -1 }) - .exec(); - } -); - -GoodsSchema.static( - "countGoodsByUids", - async (uids: ObjectId | ObjectId[], perNum = 1) => { - if (!isArray(uids)) { - uids = [ uids ]; - } - const flag = `count_uids_${uids.join("_")}_${perNum}`; - const count = cache.get(flag); - if (count) { - return count; - } - const conditions = getConditionsByUids(uids); - const total = await Model.count(conditions).exec(); - cache.put(flag, Math.ceil(total / perNum)); - return cache.get(flag); - } -); - -const getConditionsByCids = (cids: ObjectId[]) => { - return cids.length === 1 ? { - category: cids[0], - active: true - } : { - $or: reduce(cids, (arr, cid) => { - arr.push({ category: { $in: [ cid ] } }); - return arr; - }, []), - active: true - }; -}; - -GoodsSchema.static( - "getGoodsByCids", - (cids: ObjectId | ObjectId[], perNum = DEF_PER_COUNT, page = 1) => { - if (!isArray(cids)) { - cids = [ cids ]; - } - const conditions = getConditionsByCids(cids); - return Model.find(conditions) - .skip((page - 1) * perNum).limit(perNum) - .populate("uploader attributes") - .sort({ updatedAt: -1 }) - .exec(); - } -); - -GoodsSchema.static( - "countGoodsByCids", - async (cids: ObjectId | ObjectId[], perNum = 1) => { - if (!isArray(cids)) { - cids = [ cids ]; - } - if (cids.length === 0) { - return [ ]; - } - const flag = `count_cids_${cids.join("_")}_${perNum}`; - const count = cache.get(flag); - if (count) { - return count; - } - const conditions = getConditionsByCids(cids); - const total = await Model.count(conditions).exec(); - cache.put(flag, Math.ceil(total / perNum)); - return cache.get(flag); - } -); - -export interface IGoodModel extends M { - // By UID - getGoodsByUids( - uids: ObjectId | ObjectId[], perNum?: number, page?: number - ): Promise; - countGoodsByUids( - uids: ObjectId | ObjectId[], perNum?: number - ): Promise; - // By CIDs - getGoodsByCids( - cids: ObjectId | ObjectId[], perNum?: number, page?: number - ): Promise; - countGoodsByCids( - cids: ObjectId | ObjectId[], perNum?: number - ): Promise; -} - for (const method of MODIFY_MOTHODS) { GoodsSchema.post(method, () => { cache.clear(); }); } -export const Model = model(FLAG, GoodsSchema) as IGoodModel; +export const Model = model(FLAG, GoodsSchema) as M; diff --git a/src/models/Token.ts b/src/models/Token.ts index 3e3ed8e..023bfd5 100644 --- a/src/models/Token.ts +++ b/src/models/Token.ts @@ -59,11 +59,6 @@ for (const method of MODIFY_MOTHODS) { export const Model = model(Flag, TokensSchema) as M; -const getCount = async (userId: ObjectId): Promise => { - if (cache.get(userId.toString())) { - cache.get(userId.toString()); - } - const count = await Model.count({ user: userId }).exec(); - cache.put(userId.toString(), count); - return cache.get(userId.toString()); +const getCount = (userId: ObjectId): Promise => { + return Model.count({ user: userId }).exec(); }; diff --git a/src/models/User.ts b/src/models/User.ts index 1e8a4bc..0ab3cd7 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -61,14 +61,6 @@ const encryptStr = (pwd: string) => { }; // region static methods -UsersSchema.static("countUsers", async (perNum = 1) => { - const FLAG = `page_count_${perNum}`; - if (cache.get(FLAG)) { - return cache.get(FLAG); - } - cache.put(FLAG, Math.ceil((await Model.count({ }).exec()) / perNum)); - return cache.get(FLAG); -}); UsersSchema.static("addUser", (username: string, password: string) => { const newObj = { @@ -88,12 +80,6 @@ UsersSchema.static("removeUser", (id: ObjectId) => { }); }); -UsersSchema.static("list", (perNum = DEF_PER_COUNT, page = 1) => { - return Model.find().select("-password") - .skip((page - 1) * perNum).limit(perNum) - .exec(); -}); - UsersSchema.static("passwd", (id: ObjectId, oldP: string, newP: string) => { return Model.findById(id).exec() .then((result) => { @@ -152,14 +138,6 @@ interface IUserModel extends M { * @return {Promise} */ removeUser(id: ObjectId): Promise; - /** - * 获取用户列表 - * - * @param perNum {number} 每页数量 - * @param page {number} 页数 - * @return {Promise} - */ - list(perNum?: number, page?: number): Promise; /** * 修改用户密码 * @@ -177,10 +155,6 @@ interface IUserModel extends M { * @return {Promise} */ isVaild(username: string, password: string): Promise; - /** - * 返回总页数 - */ - countUsers(perNum?: number): Promise; } // region Validators diff --git a/src/modules/categroies/categroies.controller.ts b/src/modules/categroies/categroies.controller.ts index ebc21a7..6c71d10 100644 --- a/src/modules/categroies/categroies.controller.ts +++ b/src/modules/categroies/categroies.controller.ts @@ -1,19 +1,17 @@ import { Controller, Post, Res, Body, Get, HttpStatus, HttpCode, Param, - BadRequestException, UseGuards, Delete, Query + BadRequestException, UseGuards, Delete, Query, BadGatewayException } from "@nestjs/common"; import { ApiBearerAuth, ApiUseTags, ApiResponse, ApiImplicitParam, ApiOperation } from "@nestjs/swagger"; -import { - Model as CategoriesModel, CategoryDoc, ICategory -} from "@models/Categroy"; import { Model as ValuesModel, ValueDoc, IValues } from "@models/Value"; import { Roles } from "@decorators/roles"; import { RolesGuard } from "@guards/roles"; import { ParseIntPipe } from "@pipes/parse-int"; import { PerPageDto, ListResponse } from "@dtos/page"; import { CidDto } from "@dtos/ids"; +import { DefResDto } from "@dtos/res"; import { CategoriesService } from "@services/categories"; import { GoodsService } from "@services/goods"; import { UtilService } from "@services/util"; @@ -48,9 +46,9 @@ export class CategoriesAdminController { }) // endregion Swagger Docs public async list(@Query(new ParseIntPipe()) query: PerPageDto) { - const arr = await CategoriesModel.list(query.perNum, query.page); + const arr = await this.categoriesSvr.list(query); return UtilService.toListRespone(arr, Object.assign({ - total: await CategoriesModel.countCategories() + total: await this.categoriesSvr.count() }, query)); } @@ -61,7 +59,7 @@ export class CategoriesAdminController { @ApiOperation({ title: "Add Category" }) // endregion Swagger Docs public async add(@Body() ctx: NewCategoryDto) { - if (ctx.pid && !(await CategoriesModel.findById(ctx.pid).exec())) { + if (ctx.pid && !(await this.categoriesSvr.getById(ctx.pid))) { throw new BadRequestException("The Parent Category isnt exist!"); } let attrsIds = [ ]; @@ -89,7 +87,7 @@ export class CategoriesAdminController { } let result; try { - result = await CategoriesModel.create({ + result = await this.categoriesSvr.add({ name: ctx.name, tags: ctx.tags, attributes: attrsIds, @@ -113,14 +111,19 @@ export class CategoriesAdminController { public async get( @Param() param: CidDto, @Query(new ParseIntPipe()) query: PerPageDto ) { - let obj: ICategory; + let obj; try { - const doc = await CategoriesModel.findById(param.cid) - .populate("attributes") - .populate({ - path: "pid", populate: { path: "pid" } - }) - .exec(); + const doc = await this.categoriesSvr.getById(param.cid, { + populate: [ + "attributes", + { + path: "pid", populate: { path: "pid" } + } + ] + }); + if (!obj) { + return doc; + } obj = doc.toObject(); } catch (error) { throw new BadRequestException(error.toString()); @@ -142,9 +145,14 @@ export class CategoriesAdminController { public async addAttr( @Param() param: CidDto, @Body() ctx: CreateValueDto ) { - const curCategory = ( - await CategoriesModel.findById(param.cid).populate("attributes").exec() - ).toObject(); + const category = + await this.categoriesSvr.getById(param.cid, { + populate: [ "attributes" ] + }); + if (!category) { + throw new BadGatewayException("Non Exist Category"); + } + const curCategory = category.toObject(); const attributes = curCategory.attributes as IValues[]; if (attributes.length !== 0) { const attrSet = new Set(); @@ -160,9 +168,9 @@ export class CategoriesAdminController { } } const newAttr = await ValuesModel.create(ctx); - await CategoriesModel.findByIdAndUpdate( + await this.categoriesSvr.editById( param.cid, { $push: { attributes: newAttr._id } } - ).exec(); + ); return newAttr; } @@ -180,7 +188,7 @@ export class CategoriesAdminController { } catch (error) { throw new BadRequestException(error.toString()); } - return { statusCode: HttpStatus.OK }; + return new DefResDto(); } @Roles("admin") @@ -206,19 +214,15 @@ export class CategoriesAdminController { }) // endregion Swagger Docs public async deleteAttrByGet(@Param() param: CategoryAttributeParamDto) { - try { - await CategoriesModel.findByIdAndUpdate(param.cid, { - $pull: { attributes: param.aid} - }).exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + await this.categoriesSvr.editById(param.cid, { + $pull: { attributes: param.aid} + }); try { await ValuesModel.findByIdAndRemove(param.aid).exec(); } catch (error) { - await CategoriesModel.findByIdAndUpdate( + await this.categoriesSvr.editById( param.cid, { $push: { attributes: param.aid } } - ).exec(); + ); throw new BadRequestException(error.toString()); } return { status: HttpStatus.OK }; @@ -233,32 +237,36 @@ export class CategoriesAdminController { public async edit( @Param() param: CidDto, @Body() ctx: EditCategoryDto ) { - const curCategory = ( - await CategoriesModel.findById(param.cid) - .populate("attributes") - .populate({ - path: "pid", populate: { path: "pid" } - }).exec() - ).toObject(); + const curCategory = + await this.categoriesSvr.getById(param.cid, { + populate: [ + "attributes", + { + path: "pid", populate: { path: "pid" } + } + ] + }); + if (!curCategory) { + throw new BadGatewayException("Non Exist Category"); + } let parentCategory; if (ctx.pid) { - parentCategory = await CategoriesModel.findById(ctx.pid) - .populate("attributes") - .populate({ - path: "pid", populate: { path: "pid" } - }).exec(); + parentCategory = await this.categoriesSvr.getById(ctx.pid, { + populate: [ + "attributes", + { + path: "pid", populate: { path: "pid" } + } + ] + }); if (!parentCategory) { throw new BadRequestException( "The Parent Category isnt exist!" ); } } - try { - await CategoriesModel.findByIdAndUpdate(param.cid, ctx).exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } - return { status: HttpStatus.OK }; + await this.categoriesSvr.editById(param.cid, ctx); + return new DefResDto(); } @Roles("admin") @@ -284,11 +292,7 @@ export class CategoriesAdminController { }) // endregion Swagger Docs public async deleteByGet(@Param() param: CidDto) { - try { - await CategoriesModel.findByIdAndRemove(param.cid).exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } - return { status: HttpStatus.OK }; + await this.categoriesSvr.removeById(param.cid); + return new DefResDto(); } } diff --git a/src/modules/collections/collections.admin.controller.ts b/src/modules/collections/collections.admin.controller.ts index 7901d8d..04fcacf 100644 --- a/src/modules/collections/collections.admin.controller.ts +++ b/src/modules/collections/collections.admin.controller.ts @@ -102,7 +102,7 @@ export class CollectionsAdminController { @ApiOperation({ title: "Get One Collection's Info" }) // endregion Swagger Docs public async getCollection(@Param() param: CCidDto) { - const doc = await this.collectionsSvr.getByCid(param.cid); + const doc = await this.collectionsSvr.getById(param.cid); if (!doc) { return null; } diff --git a/src/modules/collections/collections.controller.ts b/src/modules/collections/collections.controller.ts index 99c6e77..bb3fea1 100644 --- a/src/modules/collections/collections.controller.ts +++ b/src/modules/collections/collections.controller.ts @@ -8,6 +8,7 @@ import { RolesGuard } from "@guards/roles"; import { Roles } from "@decorators/roles"; import { GetCollectionNameDto } from "./collections.dto"; import { PerPageDto } from "@dtos/page"; +import { ParseIntPipe } from "@pipes/parse-int"; @UseGuards(RolesGuard) @Controller("/collections") @@ -25,7 +26,8 @@ export class CollectionsController { @ApiOperation({ title: "Get Collection Info" }) // endregion Swagger Docs public async getCollection( - @Param() param: GetCollectionNameDto, @Query() query: PerPageDto + @Param() param: GetCollectionNameDto, + @Query(new ParseIntPipe()) query: PerPageDto ) { const doc = await this.collectionsSvr.getByName(param.name); if (!doc) { diff --git a/src/modules/common/services/base.service.ts b/src/modules/common/services/base.service.ts index 91c70b9..30c07ce 100644 --- a/src/modules/common/services/base.service.ts +++ b/src/modules/common/services/base.service.ts @@ -1,8 +1,14 @@ import { DEF_PER_COUNT } from "@dtos/page"; import { UtilService } from "@services/util"; +import { DocumentQuery, ModelPopulateOptions } from "mongoose"; type TimeType = number | string; +export interface IGetOptions { + populate?: Array; + select?: string[]; +} + export abstract class BaseService { private cache; @@ -14,6 +20,7 @@ export abstract class BaseService { protected loadAndCache( FLAG: string, value: () => T, time?: TimeType ): T { + /* istanbul ignore else */ if (!this.cache) { return value(); } @@ -39,4 +46,16 @@ export abstract class BaseService { */ public calPageCount = UtilService.calPageCount; + public documentQueryProcess>( + query: T, opts: IGetOptions = { } + ) { + for (const p of (opts.populate || [ ])) { + query = query.populate(p); + } + for (const s of (opts.select || [ ])) { + query = query.select(s); + } + return query; + } + } diff --git a/src/modules/common/services/categories.service.ts b/src/modules/common/services/categories.service.ts index 8bc9a26..ec929da 100644 --- a/src/modules/common/services/categories.service.ts +++ b/src/modules/common/services/categories.service.ts @@ -1,8 +1,9 @@ -import { Component } from "@nestjs/common"; +import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; -import { Model as CategoriesModel, cache } from "@models/Categroy"; -import { isFunction } from "util"; -import { BaseService } from "@services/base"; +import { Model as CategoriesModel, cache, ICategoryRaw, ICategory } from "@models/Categroy"; +import { isFunction, isArray } from "util"; +import { BaseService, IGetOptions } from "@services/base"; +import { difference } from "lodash"; interface IIdMap { [parentId: string]: ObjectId[]; @@ -51,4 +52,95 @@ export class CategoriesService extends BaseService { return ids; } + private getTags(obj: ICategoryRaw | ICategory) { + const tags = obj.tags; + const pid = obj.pid as ICategoryRaw | void; + if (pid && pid.tags) { + return tags.concat(this.getTags(pid)); + } + return tags; + } + + public async getByTags(tags: string | string[]) { + if (!isArray(tags)) { + tags = [ tags ]; + } + if (tags.length === 0) { + return Promise.resolve([ ]); + } + const conditions = tags.length === 1 ? { + tags: { $in: tags } + } : { + $or: tags.reduce((arr, tag) => { + arr.push({ tags: { $in: [ tag ] } }); + return arr; + }, []) + }; + const p = (await this.get(conditions, { + populate: [ + "attributes", + { path: "pid", populate: { path: "pid" } } + ] + })) + .map((item) => item.toObject()) + .map((item) => { + item.tags = Array.from(new Set(this.getTags(item))); + delete item.pid; + return item; + }) + .filter((item) => { + const diffLength = difference(item.tags, tags).length; + return diffLength + tags.length === item.tags.length ; + }); + return p; + } + + /** + * Category 列表 + * @param opts.perNum {number} 每页数量 + * @param opts.page {number} 页数 + * @return {Promise} + */ + public async list(opts = this.DEF_PER_OBJ) { + return CategoriesModel.find({ }) + .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) + .exec(); + } + + public count() { + return CategoriesModel.count({ }).exec(); + } + + public add(ctx: object) { + return CategoriesModel.create(ctx); + } + + public get(cond: object, opts?: IGetOptions) { + let p = CategoriesModel.find(cond); + p = this.documentQueryProcess(p, opts); + return p.exec(); + } + + public getById(id: ObjectId, opts?: IGetOptions) { + let p = CategoriesModel.findById(id); + p = this.documentQueryProcess(p, opts); + return p.exec(); + } + + public editById(id: ObjectId, ctx: object) { + try { + return CategoriesModel.findByIdAndUpdate(id, ctx).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public removeById(id: ObjectId) { + try { + return CategoriesModel.findByIdAndRemove(id).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + } diff --git a/src/modules/common/services/collections.service.ts b/src/modules/common/services/collections.service.ts index 5ade3c5..8dd5a8c 100644 --- a/src/modules/common/services/collections.service.ts +++ b/src/modules/common/services/collections.service.ts @@ -4,7 +4,7 @@ import { Model as CollectionsModel, cache } from "@models/Collection"; import { ObjectId } from "@models/common"; import { DEF_PER_COUNT } from "@dtos/page"; import { IEditCollection } from "../../../modules/collections/collections.dto"; -import { BaseService } from "@services/base"; +import { BaseService, IGetOptions } from "@services/base"; @Component() export class CollectionsService extends BaseService { @@ -56,13 +56,17 @@ export class CollectionsService extends BaseService { ); } - public getByName(name: string, opts = this.GetOptions) { + private readonly GET_OPTIONS: IGetOptions = { + populate: [ "creator", "goods" ] + }; + + /** + * Get By Collection Name + * @param name Collection Name + */ + public getByName(name: string, opts = this.GET_OPTIONS) { let p = CollectionsModel.findOne({ name }); - if (opts.populate && opts.populate.length > 0) { - for (const field of opts.populate) { - p = p.populate(field); - } - } + p = this.documentQueryProcess(p, opts); return this.loadAndCache( `getByName_${name}`, () => p.exec(), @@ -70,19 +74,15 @@ export class CollectionsService extends BaseService { ); } - private readonly GetOptions = { - populate: [ "creator", "goods" ] - }; - - public getByCid(cid: ObjectId, opts = this.GetOptions) { - let p = CollectionsModel.findById(cid); - if (opts.populate && opts.populate.length > 0) { - for (const field of opts.populate) { - p = p.populate(field); - } - } + /** + * Get By Collection ID + * @param id Collection ID + */ + public getById(id: ObjectId, opts = this.GET_OPTIONS) { + let p = CollectionsModel.findById(id); + p = this.documentQueryProcess(p, opts); return this.loadAndCache( - `getById_${cid.toString()}`, + `getById_${id.toString()}`, () => p.exec(), 7200 ); diff --git a/src/modules/common/services/goods.service.ts b/src/modules/common/services/goods.service.ts index 37d5bd5..f1cf720 100644 --- a/src/modules/common/services/goods.service.ts +++ b/src/modules/common/services/goods.service.ts @@ -1,11 +1,31 @@ -import { Component } from "@nestjs/common"; +import { Component, BadRequestException } from "@nestjs/common"; import { Model as GoodsModels } from "@models/Good"; import { ObjectId } from "@models/common"; -import { BaseService } from "@services/base"; +import { BaseService, IGetOptions } from "@services/base"; +import { isArray } from "util"; @Component() export class GoodsService extends BaseService { + private getConditionsByCids(cids: ObjectId[]) { + return cids.length === 1 ? { + category: cids[0], + active: true + } : { + $or: cids.reduce((arr, cid) => { + arr.push({ category: { $in: [ cid ] } }); + return arr; + }, [ ]), + active: true + }; + } + + public get(cond: object, opts?: IGetOptions) { + let p = GoodsModels.findOne(cond); + p = this.documentQueryProcess(p, opts); + return p.exec(); + } + public listByCategoryId(cid: ObjectId, pageObj = this.DEF_PER_OBJ) { return this.loadAndCache( `list_category_${cid.toString()}`, @@ -17,4 +37,111 @@ export class GoodsService extends BaseService { ); } + public async countByCids(cids: ObjectId | ObjectId[]) { + if (!isArray(cids)) { + cids = [ cids ]; + } + if (cids.length === 0) { + return [ ]; + } + const conditions = this.getConditionsByCids(cids); + return await GoodsModels.count(conditions).exec(); + } + + public async countByUids( + uids: ObjectId | ObjectId[], perNum = this.DEF_PER_OBJ.perNum + ) { + if (!isArray(uids)) { + uids = [ uids ]; + } + const conditions = this.getConditionsByUids(uids); + return await GoodsModels.count(conditions).exec(); + } + + /** + * Get Goods by Category ID(s) + * @param cids Category ID(s) + */ + public getByCids( + cids: ObjectId | ObjectId[], opts = this.DEF_PER_OBJ + ) { + if (!isArray(cids)) { + cids = [ cids ]; + } + const conditions = this.getConditionsByCids(cids); + return GoodsModels.find(conditions) + .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) + .populate("uploader attributes") + .sort({ updatedAt: -1 }) + .exec(); + } + + private getConditionsByUids(uids: ObjectId[]) { + let conditions; + switch (uids.length) { + case 0: + conditions = { }; + break; + case 1: + conditions = { + uploader: uids[0] + }; + break; + default: + conditions = { + $or: uids.reduce((arr, uid) => { + arr.push({ uploader: uid }); + return arr; + }, [ ]) + }; + break; + } + return conditions; + } + + /** + * Get Goods by User ID(s) + * @param uids User ID(s) + */ + public getByUids( + uids: ObjectId | ObjectId[], opts = this.DEF_PER_OBJ + ) { + if (!isArray(uids)) { + uids = [ uids ]; + } + const conditions = this.getConditionsByUids(uids); + return GoodsModels.find(conditions) + .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) + .select("-uploader") + .populate("attributes") + .sort({ updatedAt: -1 }) + .exec(); + } + + /** + * Get Good by Good ID + * @param id Good ID + */ + public getById(id: ObjectId, opts?: IGetOptions) { + let p = GoodsModels.findById(id); + p = this.documentQueryProcess(p, opts); + return p.exec(); + } + + /** + * Edit Good by Good ID + * @param id Good ID + */ + public async editById(id: ObjectId, ctx: object) { + try { + await GoodsModels.findByIdAndUpdate(id, ctx).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public add(ctx: object) { + return GoodsModels.create(ctx); + } + } diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index a665970..b017dc6 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -6,7 +6,7 @@ import { import { Model as CategroiesModel, ICategory } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; import { isUndefined } from "util"; -import { BaseService } from "@services/base"; +import { BaseService, IGetOptions } from "@services/base"; export interface IGetRegexpsOptions { categroies?: ObjectId[]; @@ -42,8 +42,9 @@ export class RegexpsService extends BaseService { /** * 修改规则 + * @param id Regexp ID */ - public async editById(id: ObjectId, obj) { + public async editById(id: ObjectId, obj: object) { try { return await RegexpsModel .update( @@ -100,10 +101,11 @@ export class RegexpsService extends BaseService { ); } - public getRegexp(id: ObjectId) { - return RegexpsModel.findById(id) - .populate({ path: "link", populate: { path: "pid" } }) - .exec(); + public getById(id: ObjectId, opts?: IGetOptions) { + let p = RegexpsModel.findById(id) + .populate({ path: "link", populate: { path: "pid" } }); + p = this.documentQueryProcess(p, opts); + return p.exec(); } /** diff --git a/src/modules/common/services/tokens.service.ts b/src/modules/common/services/tokens.service.ts index 913a29a..cb12b89 100644 --- a/src/modules/common/services/tokens.service.ts +++ b/src/modules/common/services/tokens.service.ts @@ -7,8 +7,12 @@ import { IUidDto } from "@dtos/ids"; @Component() export class TokensService { - public create(obj) { - return TokensModel.create(obj); + public async create(obj) { + try { + return await TokensModel.create(obj); + } catch (error) { + throw new BadRequestException(error.toString()); + } } public getRawTokens(uid: ObjectId) { @@ -36,8 +40,12 @@ export class TokensService { } } - public remove(obj) { - return TokensModel.findOneAndRemove(obj).exec(); + public async remove(obj) { + try { + return await TokensModel.findOneAndRemove(obj).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } } /** diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index beeecf8..356f568 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -51,11 +51,6 @@ export class UsergroupsService extends BaseService { return UsergroupsModel.count({ }).exec(); } - public async countPage(perNum = this.DEF_PER_OBJ.perNum) { - const total = await this.count(); - return Math.ceil(total / perNum); - } - public list(pageObj = this.DEF_PER_OBJ) { const perNum = pageObj.perNum || this.DEF_PER_OBJ.perNum; const page = pageObj.page || this.DEF_PER_OBJ.page; @@ -65,12 +60,20 @@ export class UsergroupsService extends BaseService { .exec(); } + /** + * Remove Usergroup By Usergroup ID + * @param gid Usergroup ID + */ public async remove(gid: ObjectId) { if ((await this.count()) === 1) { - throw new BadRequestException("Nnn delete unique group"); + throw new BadRequestException("Cant delete unique group"); } try { - return await UsergroupsModel.findByIdAndRemove(gid).exec(); + const p = await UsergroupsModel.findByIdAndRemove(gid).exec(); + await UserUsergroupsModel.findOneAndRemove({ + usergroup: gid + }).exec(); + return p; } catch (error) { throw new BadRequestException(error.toString()); } diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index d06a483..99ca1fb 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -4,7 +4,7 @@ import { Model as UsersModel, UserDoc } from "@models/User"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { IUsergroups } from "@models/Usergroup"; import { SystemService } from "@services/system"; -import { BaseService } from "@services/base"; +import { BaseService, IGetOptions } from "@services/base"; @Component() export class UsersService extends BaseService { @@ -37,6 +37,14 @@ export class UsersService extends BaseService { } } + public async isVaild(username: string, password: string) { + try { + return await UsersModel.isVaild(username, password); + } catch (err) { + throw new BadRequestException(err.toString()); + } + } + public countUsergroups(uid: ObjectId) { return UserUsergroupsModel.count({ user: uid }).exec(); } @@ -56,13 +64,15 @@ export class UsersService extends BaseService { } /** - * 修改`User`属性, 除了`username` + * 修改`User`属性, 除了`username`和`password` * @param id User ID * @param content Content */ public async modify(id: ObjectId, content): Promise { - if (content && content.username) { - delete content.username; + for (const field of [ "username", "password" ]) { + if (content && content[field]) { + delete content[field]; + } } if (Object.keys(content).length === 0) { throw new BadRequestException("Empty Content"); @@ -75,4 +85,41 @@ export class UsersService extends BaseService { throw new BadRequestException(error.toString()); } } + + public async passwd(id: ObjectId, oldPass: string, newPass: string) { + try { + return await UsersModel.passwd(id, oldPass, newPass); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + /** + * 返回总数 + */ + public conut() { + return UsersModel.count({ }).exec(); + } + + /** + * 获取用户列表 + * + * @param opts.perNum 每页数量 + * @param opts.page {number} 页数 + */ + public list(opts = this.DEF_PER_OBJ) { + return UsersModel.find().select("-password") + .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) + .exec(); + } + + /** + * Get User By User ID + * @param id User ID + */ + public getById(id: ObjectId, opts?: IGetOptions) { + let p = UsersModel.findById(id); + p = this.documentQueryProcess(p, opts); + return p.exec(); + } } diff --git a/src/modules/files/files.controller.ts b/src/modules/files/files.controller.ts index 13805d9..ff54425 100644 --- a/src/modules/files/files.controller.ts +++ b/src/modules/files/files.controller.ts @@ -3,11 +3,12 @@ import { NotFoundException, UseGuards, Query, HttpStatus, HttpCode } from "@nestjs/common"; import { ApiUseTags, ApiImplicitParam, ApiOperation } from "@nestjs/swagger"; -import { Model as GoodsModels, GoodDoc } from "@models/Good"; +import { GoodDoc } from "@models/Good"; import { config } from "@utils/config"; import { Roles } from "@decorators/roles"; import { RolesGuard } from "@guards/roles"; import { ParseIntPipe } from "@pipes/parse-int"; +import { GoodsService } from "@services/goods"; import { Response } from "express"; import pathExists = require("path-exists"); @@ -25,6 +26,8 @@ import { DownlaodDto } from "./files.dto"; @ApiUseTags("Good Download") export class FilesController { + constructor(private readonly goodsSvr: GoodsService) { } + @Roles("guest") @Get("/categories/:cid/goods/:id") // region Swagger Docs @@ -37,9 +40,9 @@ export class FilesController { ) { let obj: GoodDoc; try { - obj = await GoodsModels - .findOne({_id: params.id, category: params.cid}) - .exec(); + obj = await this.goodsSvr.get({ + _id: params.id, category: params.cid + }); } catch (error) { throw new BadRequestException(error.toString()); } diff --git a/src/modules/files/goods.controller.ts b/src/modules/files/goods.controller.ts index 7917d0b..2fbedcf 100644 --- a/src/modules/files/goods.controller.ts +++ b/src/modules/files/goods.controller.ts @@ -2,12 +2,12 @@ import { UseGuards, Controller, Get, HttpCode, HttpStatus, Query } from "@nestjs/common"; import { ApiUseTags, ApiOperation, ApiResponse } from "@nestjs/swagger"; -import { Model as CategoriesModel } from "@models/Categroy"; -import { Model as GoodsModels } from "@models/Good"; import { UtilService } from "@services/util"; import { IUser } from "@models/User"; import { IGoodsRaw } from "@models/Good"; import { RolesGuard } from "@guards/roles"; +import { GoodsService } from "@services/goods"; +import { CategoriesService } from "@services/categories"; import { Roles } from "@decorators/roles"; import { ParseIntPipe } from "@pipes/parse-int"; import { ListResponse, DEF_PER_COUNT } from "@dtos/page"; @@ -20,6 +20,11 @@ import { GoodsQueryDto } from "./goods.dto"; @ApiUseTags("Good Download") export class GoodsController { + constructor( + private readonly goodsSvr: GoodsService, + private readonly categoriesSvr: CategoriesService + ) { } + @Roles("guest") @Get() // region Swagger Docs @@ -28,7 +33,7 @@ export class GoodsController { @ApiResponse({ status: HttpStatus.OK, type: ListResponse }) // endregion Swagger Docs public async getList(@Query(new ParseIntPipe()) query: GoodsQueryDto) { - const categoryModels = await CategoriesModel.getCategories(query.tags); + const categoryModels = await this.categoriesSvr.getByTags(query.tags); const categories = reduce(categoryModels, (obj, cate) => { obj[cate._id.toString()] = cate; return obj; @@ -40,7 +45,7 @@ export class GoodsController { const cids = Object.keys(categories); const goods = - (await GoodsModels.getGoodsByCids(cids, perNum, query.page)) + (await this.goodsSvr.getByCids(cids, query)) .map((doc) => { const good = doc.toObject() as IGoodsRaw; const category = categories[good.category.toString()]; @@ -54,7 +59,7 @@ export class GoodsController { return good; }); return UtilService.toListRespone(goods, Object.assign({ - total: await GoodsModels.countGoodsByCids(cids) + total: await this.goodsSvr.countByCids(cids) }, query)); } } diff --git a/src/modules/goods/goods.controller.ts b/src/modules/goods/goods.controller.ts index bf663fe..ce80301 100644 --- a/src/modules/goods/goods.controller.ts +++ b/src/modules/goods/goods.controller.ts @@ -7,11 +7,7 @@ import { ApiImplicitBody, ApiConsumes } from "@nestjs/swagger"; import { IValues, Model as ValuesModel } from "@models/Value"; -import { Model as GoodsModels, IGoods } from "@models/Good"; -import { Model as RegexpModel } from "@models/Regexp"; -import { Model as TokensModel } from "@models/Token"; -import { Model as CollectionsModel } from "@models/Collection"; -import { Model as CategroiesModel } from "@models/Categroy"; +import { IGoods } from "@models/Good"; import { ObjectId } from "@models/common"; import { config } from "@utils/config"; import { RolesGuard } from "@guards/roles"; @@ -20,6 +16,7 @@ import { File, Files, User } from "@decorators/route"; import { GidDto } from "@dtos/ids"; import { IReqUser } from "@dtos/req"; import { PerPageDto, ListResponse } from "@dtos/page"; +import { DefResDto } from "@dtos/res"; import { RegexpCountCheckPipe } from "@pipes/regexp-count-check"; import { ParseIntPipe } from "@pipes/parse-int"; import { ToArrayPipe } from "@pipes/to-array"; @@ -27,6 +24,7 @@ import { TokensService } from "@services/tokens"; import { CollectionsService } from "@services/collections"; import { IGetRegexpsOptions, RegexpsService } from "@services/regexps"; import { CategoriesService } from "@services/categories"; +import { GoodsService } from "@services/goods"; import { UtilService } from "@services/util"; import * as hasha from "hasha"; import fs = require("fs-extra"); @@ -47,7 +45,8 @@ export class GoodsAdminController { private readonly tokensSvr: TokensService, private readonly collectionsSvr: CollectionsService, private readonly regexpSvr: RegexpsService, - private readonly categoriesSvr: CategoriesService + private readonly categoriesSvr: CategoriesService, + private readonly goodsSvr: GoodsService ) { } private toMd5sum(filepath: string) { @@ -69,11 +68,11 @@ export class GoodsAdminController { }) // endregion Swagger Docs public async getGoods(@Query(new ParseIntPipe()) query: PerPageDto) { - const arr = await GoodsModels.getGoodsByUids( - [ ], query.perNum, query.page + const arr = await this.goodsSvr.getByUids( + [ ], query ); return UtilService.toListRespone(arr, Object.assign({ - total: await GoodsModels.countGoodsByUids([ ]) + total: await this.goodsSvr.countByUids([ ]) }, query)); } @@ -87,7 +86,7 @@ export class GoodsAdminController { return arr; }, [ ]) }; - const categories = await CategroiesModel.find(conditions).exec(); + const categories = await this.categoriesSvr.get(conditions); const idSet = new Set(); for (const category of categories) { const id = category._id.toString(); @@ -124,7 +123,7 @@ export class GoodsAdminController { try { const md5sum = this.toMd5sum(obj.file.path); const sha256sum = this.toSha256sum(obj.file.path); - goodObj = (await GoodsModels.create({ + goodObj = (await this.goodsSvr.add({ filename: obj.file.filename, originname: obj.file.originalname, category: categories[0]._id, @@ -230,10 +229,9 @@ export class GoodsAdminController { }); const collection = isArray(collections) ? collections[0] : collections; - return CollectionsModel - .findById(collection._id) - .populate("goods") - .exec(); + return this.collectionsSvr.getById(collection._id, { + populate: [ "goods" ] + }); } catch (error) { throw new BadRequestException(error.toString()); } @@ -249,11 +247,13 @@ export class GoodsAdminController { public async get(@Param() param: GidDto) { let obj; try { - obj = await GoodsModels.findById(param.gid) - .populate("uploader", "nickname") - .populate("attributes") - .populate("category", "name attributes tags") - .exec(); + obj = await this.goodsSvr.getById(param.gid, { + populate: [ + "attributes", + { path: "uploader", select: "nickname" }, + { path: "category", select: "name attributes tags" } + ] + }); } catch (error) { throw new BadRequestException(error.toString()); } @@ -269,11 +269,11 @@ export class GoodsAdminController { public async addAttr( @Param() param: GidDto, @Body() ctx: CreateValueDto ) { - const obj = await GoodsModels.findById(param.gid) - .populate("attributes") - .exec(); + const obj = await this.goodsSvr.getById(param.gid, { + populate: [ "attributes" ] + }); if (!obj) { - // TODO throw + throw new BadRequestException("Non Exist Good"); } const attributes = obj.toObject().attributes as IValues[]; if (attributes.length !== 0) { @@ -287,9 +287,9 @@ export class GoodsAdminController { } } const newAttr = await ValuesModel.create(ctx); - await GoodsModels.findByIdAndUpdate( + await this.goodsSvr.editById( param.gid, { $push: { attributes: newAttr._id } } - ).exec(); + ); return { statusCode: HttpStatus.CREATED }; } @@ -307,7 +307,7 @@ export class GoodsAdminController { } catch (error) { throw new BadRequestException(error.toString()); } - return { statusCode: HttpStatus.OK }; + return new DefResDto(); } @Roles("admin") @@ -333,22 +333,18 @@ export class GoodsAdminController { }) // endregion Swagger Docs public async deleteAttrByGet(@Param() param: GoodAttributeParamDto) { - try { - await GoodsModels.findByIdAndUpdate(param.gid, { - $pull: { attributes: param.aid} - }).exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + await this.goodsSvr.editById(param.gid, { + $pull: { attributes: param.aid} + }); try { await ValuesModel.findByIdAndRemove(param.aid).exec(); } catch (error) { - await GoodsModels.findByIdAndUpdate( + await this.goodsSvr.editById( param.gid, { $push: { attributes: param.aid } } - ).exec(); + ); throw new BadRequestException(error.toString()); } - return { statusCode: HttpStatus.OK }; + return new DefResDto(); } } diff --git a/src/modules/regexps/regexps.controller.ts b/src/modules/regexps/regexps.controller.ts index 30af327..a418ef4 100644 --- a/src/modules/regexps/regexps.controller.ts +++ b/src/modules/regexps/regexps.controller.ts @@ -16,6 +16,7 @@ import { RidDto } from "@dtos/ids"; import { ParseIntPipe } from "@pipes/parse-int"; import { RegexpsService } from "@services/regexps"; import { UtilService } from "@services/util"; +import { DefResDto } from "@dtos/res"; @UseGuards(RolesGuard) @Controller("api/v1/regexps") @@ -50,8 +51,8 @@ export class RegexpsAdminController { @Post() @HttpCode(HttpStatus.CREATED) @ApiOperation({ title: "Add RegExp" }) - public async add(@Body() ctx: NewRegexp) { - return await this.regexpsSvr.create(ctx); + public add(@Body() ctx: NewRegexp) { + return this.regexpsSvr.create(ctx); } @Roles("admin") @@ -61,7 +62,7 @@ export class RegexpsAdminController { @ApiOperation({ title: "Get RegExp Info" }) // endregion Swagger Docs public getRegexp(@Param() param: RidDto) { - return this.regexpsSvr.getRegexp(param.rid); + return this.regexpsSvr.getById(param.rid); } @Roles("admin") @@ -85,7 +86,7 @@ export class RegexpsAdminController { if (!regexp) { throw new BadRequestException("NonExist RegExp"); } - return { statusCode: HttpStatus.OK }; + return new DefResDto(); } @Roles("admin") @@ -94,7 +95,8 @@ export class RegexpsAdminController { @HttpCode(HttpStatus.OK) @ApiOperation({ title: "Delete RegExp" }) @ApiResponse({ - status: HttpStatus.OK, description: "Delete RegExp Success" + status: HttpStatus.OK, description: "Delete RegExp Success", + type: DefResDto }) // endregion Swagger Docs public deleteByDelete(@Param() param: RidDto) { @@ -107,12 +109,13 @@ export class RegexpsAdminController { @HttpCode(HttpStatus.OK) @ApiOperation({ title: "Delete RegExp" }) @ApiResponse({ - status: HttpStatus.OK, description: "Delete RegExp Success" + status: HttpStatus.OK, description: "Delete RegExp Success", + type: DefResDto }) // endregion Swagger Docs public async deleteByGet(@Param() param: RidDto) { await this.regexpsSvr.remove(param.rid); - return { statusCode: HttpStatus.OK }; + return new DefResDto(); } } diff --git a/src/modules/tokens/tokens.controller.ts b/src/modules/tokens/tokens.controller.ts index 383b167..b0cf67a 100644 --- a/src/modules/tokens/tokens.controller.ts +++ b/src/modules/tokens/tokens.controller.ts @@ -3,7 +3,6 @@ import { BadRequestException, ForbiddenException } from "@nestjs/common"; import { ApiUseTags, ApiResponse, ApiOperation } from "@nestjs/swagger"; -import { Model as TokensModel } from "@models/Token"; import { Roles } from "@decorators/roles"; import { RolesGuard } from "@guards/roles"; import { TokensService } from "@services/tokens"; @@ -66,16 +65,12 @@ export class TokensAdminController { public async deleteTokenByGet( @Param() param: TokenParamDto, @Session() session ) { - try { - const token = await this.tokensSvr.remove({ - _id: param.tid, - user: session.loginUserId - }); - if (!token) { - throw new BadRequestException("The Tokens isth exist"); - } - } catch (error) { - throw new BadRequestException(error.toString()); + const token = await this.tokensSvr.remove({ + _id: param.tid, + user: session.loginUserId + }); + if (!token) { + throw new BadRequestException("The Tokens isth exist"); } return new DefResDto(); } diff --git a/src/modules/usergroups/usergroups.controller.ts b/src/modules/usergroups/usergroups.controller.ts index d071d82..4d8b2f6 100644 --- a/src/modules/usergroups/usergroups.controller.ts +++ b/src/modules/usergroups/usergroups.controller.ts @@ -1,4 +1,7 @@ -import { UseGuards, Controller, Get, Query, HttpStatus, HttpCode, Post, Body, Param, Delete } from "@nestjs/common"; +import { + UseGuards, Controller, Get, Query, HttpStatus, HttpCode, Post, Body, Param, + Delete +} from "@nestjs/common"; import { ApiUseTags, ApiOperation, ApiResponse } from "@nestjs/swagger"; import { RolesGuard } from "@guards/roles"; import { UsergroupsService } from "@services/usergroups"; @@ -8,7 +11,9 @@ import { PerPageDto, ListResponse, DEF_PER_COUNT } from "@dtos/page"; import { UGidDto } from "@dtos/ids"; import { UtilService } from "@services/util"; -import { AddUsergroupDto, EditUsergroupDto, UserUsergroupDto } from "./usergroups.dto"; +import { + AddUsergroupDto, EditUsergroupDto, UserUsergroupDto +} from "./usergroups.dto"; @UseGuards(RolesGuard) @ApiUseTags("User Groups") diff --git a/src/modules/users/auth.controller.ts b/src/modules/users/auth.controller.ts index 0552ca7..e89a98d 100644 --- a/src/modules/users/auth.controller.ts +++ b/src/modules/users/auth.controller.ts @@ -7,10 +7,9 @@ import { } from "@nestjs/swagger"; import uuid = require("uuid"); import basicAuth = require("basic-auth"); -import { Model as UserModel, UserDoc } from "@models/User"; -import { Model as TokensModel } from "@models/Token"; import { RolesGuard } from "@guards/roles"; import { TokensService } from "@services/tokens"; +import { UsersService } from "@services/users"; import { LoginBodyDto, LoginQueryDto, LoginRespone } from "./auth.dto"; @UseGuards(RolesGuard) @@ -18,7 +17,10 @@ import { LoginBodyDto, LoginQueryDto, LoginRespone } from "./auth.dto"; @Controller("api/v1/auth") export class AuthAdminController { - constructor(private readonly tokensSvr: TokensService) { } + constructor( + private readonly usersSvr: UsersService, + private readonly tokensSvr: TokensService + ) { } @Post("login") @ApiOperation({ title: "Login System" }) @@ -31,12 +33,8 @@ export class AuthAdminController { @Session() session, @Body() ctx: LoginBodyDto, @Query() query: LoginQueryDto ) { - let user: UserDoc = null; - try { - user = await UserModel.isVaild(ctx.username, ctx.password); - } catch (err) { - throw new BadRequestException(err.toString()); - } + const user = + await this.usersSvr.isVaild(ctx.username, ctx.password);; session.loginUser = user.toObject().username; session.loginUserId = user.toObject()._id; const obj = new LoginRespone(); @@ -45,13 +43,9 @@ export class AuthAdminController { obj.id = user.toObject()._id; if (query.token) { const token = uuid(); - try { - await this.tokensSvr.create({ - token, user: session.loginUserId - }); - } catch (error) { - throw new BadRequestException(error.toString()); - } + await this.tokensSvr.create({ + token, user: session.loginUserId + }); obj.token = token; } obj.expires = session.cookie.maxAge || session.cookie.originalMaxAge; diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index f21e480..ee54c37 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -5,11 +5,6 @@ import { import { ApiBearerAuth, ApiUseTags, ApiResponse, ApiOperation, ApiImplicitParam } from "@nestjs/swagger"; -import { Model as UserModel, IUser, UserDoc } from "@models/User"; -import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; -import { Model as TokensModel } from "@models/Token"; -import { Model as GoodsModels } from "@models/Good"; -import { CollectionDoc } from "@models/Collection"; import { ObjectId } from "@models/common"; import { Roles } from "@decorators/roles"; import { RolesGuard } from "@guards/roles"; @@ -20,6 +15,7 @@ import { CollectionsService } from "@services/collections"; import { UsersService } from "@services/users"; import { SystemService } from "@services/system"; import { UtilService } from "@services/util"; +import { GoodsService } from "@services/goods"; import { PerPageDto, ListResponse, DEF_PER_COUNT } from "@dtos/page"; import { UidDto } from "@dtos/ids"; import { DefResDto } from "@dtos/res"; @@ -41,7 +37,8 @@ export class UsersAdminController { private readonly collectionsSvr: CollectionsService, private readonly usersSvr: UsersService, private readonly ugSvr: UsergroupsService, - private readonly sysSvr: SystemService + private readonly sysSvr: SystemService, + private readonly goodsSvr: GoodsService ) { } @Roles("admin") @@ -55,9 +52,9 @@ export class UsersAdminController { }) // endregion Swagger Docs public async findAll(@Query(new ParseIntPipe()) query: PerPageDto) { - const arr = await UserModel.list(query.perNum, query.page); + const arr = await this.usersSvr.list(query); return UtilService.toListRespone(arr, Object.assign({ - total: await UserModel.countUsers() + total: await this.usersSvr.conut() }, query)); } @@ -110,11 +107,9 @@ export class UsersAdminController { public async password( @Body() user: ModifyPasswordDto, @Param() param: UidDto ) { - try { - await UserModel.passwd(param.uid, user.oldPassword, user.newPassword); - } catch (error) { - throw new BadRequestException(error.toString()); - } + await this.usersSvr.passwd( + param.uid, user.oldPassword, user.newPassword + ); return new DefResDto(); } @@ -229,11 +224,9 @@ export class UsersAdminController { //////////////////////////////////////// private async getGoodsRes(uid: ObjectId, query: PerPageDto) { - const arr = await GoodsModels.getGoodsByUids( - uid, query.perNum, query.page - ); + const arr = await this.goodsSvr.getByUids(uid, query); return UtilService.toListRespone(arr, Object.assign({ - total: await GoodsModels.countGoodsByUids(uid) + total: await this.goodsSvr.countByUids(uid) }, query)); } @@ -358,7 +351,7 @@ export class UsersAdminController { @ApiResponse({ status: HttpStatus.OK, description: "Get User Info" }) // endregion Swagger Docs public get(@Param() param: UidDto) { - return UserModel.findById(param.uid).exec(); + return this.usersSvr.getById(param.uid); } } diff --git a/test/models/user.spec.ts b/test/models/user.spec.ts index 4fa45b8..c5ede62 100644 --- a/test/models/user.spec.ts +++ b/test/models/user.spec.ts @@ -58,11 +58,11 @@ describe("User Model", () => { UsersModel.removeUser(id); }); - it("User List", async () => { - const results = await UsersModel.list(); - results.should.be.an.Array(); - const users = results.map((item) => item.toObject()); - users.should.be.matchAny({ username: user.username }); + it.skip("User List", async () => { + // const results = await UsersModel.list(); + // results.should.be.an.Array(); + // const users = results.map((item) => item.toObject()); + // users.should.be.matchAny({ username: user.username }); }); it("Add User and use same username", async () => { From 65ede41f39fe68e92442f634aa370d958562a34c Mon Sep 17 00:00:00 2001 From: Arylo Date: Tue, 13 Mar 2018 18:30:21 +0800 Subject: [PATCH 25/49] Add Common Exists Validator --- src/models/Collection.ts | 14 +++------- src/models/Good.ts | 20 +++++++------ src/models/Regexp.ts | 17 ++++------- src/models/Token.ts | 12 ++++---- src/models/User.ts | 11 ++++---- src/models/Usergroup.ts | 12 ++------ src/models/common.ts | 28 ++++++++++++++++++- src/modules/common/services/base.service.ts | 4 ++- .../common/services/collections.service.ts | 6 ++-- .../common/services/regexps.service.ts | 10 +++---- .../common/services/usergroups.service.ts | 6 ++-- src/modules/common/services/users.service.ts | 6 ++-- test/helpers/database/auth.ts | 2 +- test/helpers/database/user.ts | 12 +++++++- 14 files changed, 91 insertions(+), 69 deletions(-) diff --git a/src/models/Collection.ts b/src/models/Collection.ts index 968830e..983afa7 100644 --- a/src/models/Collection.ts +++ b/src/models/Collection.ts @@ -1,5 +1,7 @@ import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; -import { Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS } from "@models/common"; +import { + Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS, existsValidator +} from "@models/common"; import { IGoods, FLAG as GoodFlag, Model as GoodsModels } from "@models/Good"; import { IUser, FLAG as UserFlag } from "@models/User"; import Cache = require("schedule-cache"); @@ -46,15 +48,7 @@ const CollectionsSchema = new Base(Definition).createSchema(); CollectionsSchema.path("name").validate({ isAsync: true, validator: async function nameValidator(val, respond) { - if (!this.isNew) { - const id = this.getQuery()._id; - const col = await Model.findById(id).exec(); - if (col.toObject().name === val) { - return respond(true); - } - } - const result = await Model.findOne({ name: val }).exec(); - respond(result ? false : true); + respond(await existsValidator(Model, "name", val)); }, message: "The name is existed" }); diff --git a/src/models/Good.ts b/src/models/Good.ts index 1b32663..9c2d8f3 100644 --- a/src/models/Good.ts +++ b/src/models/Good.ts @@ -1,5 +1,7 @@ import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; -import { Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS } from "@models/common"; +import { + Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS, existsValidator +} from "@models/common"; import { IValues, Flag as ValueFlag } from "@models/Value"; import { IUser, FLAG as UserFlag } from "@models/User"; import { ICategory, FLAG as CategoryFlag } from "@models/Categroy"; @@ -61,20 +63,20 @@ const GoodsSchema = new Base(Definition).createSchema(); // region validators GoodsSchema.path("md5sum").validate({ isAsync: true, - validator: (val, respond) => { - Model.findOne({ md5sum: val }).exec().then((result) => { - respond(result ? false : true); - }); + validator: async function md5ExistsValidator(val, respond) { + respond(await existsValidator(Model, "md5sum", val, { + update: false + })); }, message: "The file is existed" }); GoodsSchema.path("sha256sum").validate({ isAsync: true, - validator: (val, respond) => { - Model.findOne({ sha256sum: val }).exec().then((result) => { - respond(result ? false : true); - }); + validator: async function sha256ExistsValidator(val, respond) { + respond(await existsValidator(Model, "sha256sum", val, { + update: false + })); }, message: "The file is existed" }); diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index f8314ad..30de163 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -1,5 +1,7 @@ import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; -import { Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS } from "@models/common"; +import { + Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS, existsValidator +} from "@models/common"; import { ICategory, FLAG as CF, Model as CM } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; import Cache = require("schedule-cache"); @@ -123,16 +125,9 @@ RegexpSchema.path("hidden").validate({ if (!value) { // hidden === false return respond(true); } - if (!this.isNew) { // hidden === Old Value - const id = this.getQuery()._id; - const col = await Model.findById(id).exec(); - if (col.toObject().hidden === value) { - return respond(true); - } - } - const result = - await Model.findOne({ value: this.value, hidden: value }).exec(); - respond(result ? false : true); + respond(await existsValidator(Model, "hidden", value, { + extraCond: { value: this.value } + })); }, message: "Only one active item with every value" }); diff --git a/src/models/Token.ts b/src/models/Token.ts index 023bfd5..e0ab0ca 100644 --- a/src/models/Token.ts +++ b/src/models/Token.ts @@ -1,5 +1,7 @@ import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; -import { Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS } from "@models/common"; +import { + Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS, existsValidator +} from "@models/common"; import { IUser, FLAG as UserFlag } from "@models/User"; import Cache = require("schedule-cache"); @@ -33,10 +35,10 @@ const TokensSchema = new Base(Definition).createSchema(); // region validators TokensSchema.path("token").validate({ isAsync: true, - validator: (val, respond) => { - Model.findOne({ token: val }).exec().then((result) => { - respond(result ? false : true); - }); + validator: async (val, respond) => { + respond(await existsValidator(Model, "token", val, { + update: false + })); }, message: "The token is existed" }); diff --git a/src/models/User.ts b/src/models/User.ts index 0ab3cd7..dfe2123 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,7 +1,7 @@ import { model, SchemaDefinition, Model as M } from "mongoose"; import * as md5 from "md5"; import { config } from "@utils/config"; -import { ObjectId } from "@models/common"; +import { ObjectId, existsValidator } from "@models/common"; import { DEF_PER_COUNT } from "@dtos/page"; import Cache = require("schedule-cache"); import { Base, IDoc, IDocRaw, MODIFY_MOTHODS } from "./common"; @@ -32,7 +32,7 @@ const UsersSchema = new Base(Definition).createSchema(); UsersSchema.path("username").validate({ isAsync: true, validator: async function usernameModifyValidator(val, respond) { - if (!this.isNew) { + if (this && !this.isNew) { const id = this.getQuery()._id; const col = await Model.findById(id).exec(); return respond(col.toObject().username === val); @@ -45,11 +45,12 @@ UsersSchema.path("username").validate({ UsersSchema.path("username").validate({ isAsync: true, validator: async function usernameExistValidator(val, respond) { - if (!this.isNew) { + if (this && !this.isNew) { return respond(true); } - const result = await Model.findOne({ username: val }).exec(); - respond(result ? false : true); + respond( + await existsValidator(Model, "username", val, { update: false }) + ); }, message: "The username is existed" }); diff --git a/src/models/Usergroup.ts b/src/models/Usergroup.ts index fe1a7c6..608ee8a 100644 --- a/src/models/Usergroup.ts +++ b/src/models/Usergroup.ts @@ -1,6 +1,6 @@ import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; import { FLAG as UF, IUser } from "@models/User"; -import { ObjectId } from "@models/common"; +import { ObjectId, existsValidator } from "@models/common"; import { Base, IDoc, IDocRaw } from "./common"; const Definition: SchemaDefinition = { @@ -20,15 +20,7 @@ const UsergroupsSchema = new Base(Definition).createSchema(); UsergroupsSchema.path("name").validate({ isAsync: true, validator: async function nameExistValidator(val, respond) { - if (!this.isNew) { - const id = this.getQuery()._id; - const ug = await Model.findById(id).exec(); - if (ug.toObject().user === val) { - return respond(true); - } - } - const ug = await Model.findOne({ name: val }).exec(); - return respond(!ug); + return respond(await existsValidator(Model, "name", val)); }, message: "The Name is exist" }); diff --git a/src/models/common.ts b/src/models/common.ts index fbeaaa2..d874fb9 100644 --- a/src/models/common.ts +++ b/src/models/common.ts @@ -1,5 +1,5 @@ import * as lodash from "lodash"; -import { Schema, Document as Doc } from "mongoose"; +import { Schema, Document as Doc, Model as M } from "mongoose"; export type ObjectId = Schema.Types.ObjectId | string; @@ -51,3 +51,29 @@ export const MODIFY_MOTHODS = [ "findOneAndRemove", "findOneAndUpdate", "insertMany" ]; + +interface IExistsValidatorOptions { + update?: boolean; + extraCond?: object; +} + +const existsValidatorOptions = { + update: true +}; + +export async function existsValidator( + model: M, field: string, value, opts?: IExistsValidatorOptions +) { + const options = Object.assign({ }, existsValidator, opts); + if (options.update && this && !this.isNew) { + const id = this.getQuery()._id; + const col = await model.findById(id).exec(); + if (col.toObject()[field] === value) { + return true; + } + } + const cond = + Object.assign({ }, (options.extraCond || { }), { [field]: value }); + const result = await model.findOne(cond).exec(); + return !result; +} diff --git a/src/modules/common/services/base.service.ts b/src/modules/common/services/base.service.ts index 30c07ce..fbcc3ca 100644 --- a/src/modules/common/services/base.service.ts +++ b/src/modules/common/services/base.service.ts @@ -11,6 +11,9 @@ export interface IGetOptions { export abstract class BaseService { + protected readonly DEF_UPDATE_OPTIONS = { + runValidators: true, context: "query" + }; private cache; protected setCache(cache) { @@ -20,7 +23,6 @@ export abstract class BaseService { protected loadAndCache( FLAG: string, value: () => T, time?: TimeType ): T { - /* istanbul ignore else */ if (!this.cache) { return value(); } diff --git a/src/modules/common/services/collections.service.ts b/src/modules/common/services/collections.service.ts index 8dd5a8c..83ce11d 100644 --- a/src/modules/common/services/collections.service.ts +++ b/src/modules/common/services/collections.service.ts @@ -24,9 +24,9 @@ export class CollectionsService extends BaseService { public async edit(cid: ObjectId, ctx: IEditCollection) { try { - return await CollectionsModel.update({ _id: cid }, ctx, { - runValidators: true, context: "query" - }).exec(); + return await CollectionsModel + .update({ _id: cid }, ctx, this.DEF_UPDATE_OPTIONS) + .exec(); } catch (error) { throw new BadRequestException(error.toString()); } diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index b017dc6..3f4bd33 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -47,9 +47,7 @@ export class RegexpsService extends BaseService { public async editById(id: ObjectId, obj: object) { try { return await RegexpsModel - .update( - { _id: id }, obj, { runValidators: true, context: "query" } - ) + .update({ _id: id }, obj, this.DEF_UPDATE_OPTIONS) .exec(); } catch (error) { throw new BadRequestException(error.toString()); @@ -85,9 +83,9 @@ export class RegexpsService extends BaseService { throw new BadRequestException("Nonexist Categroy ID"); } try { - return await RegexpsModel.findByIdAndUpdate( - id, { link: linkId }, { runValidators: true } - ).exec(); + return await RegexpsModel + .update({ _id: id }, { link: linkId }, this.DEF_UPDATE_OPTIONS) + .exec(); } catch (error) { throw new BadRequestException(error.toSrting()); } diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index 356f568..eaff437 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -18,9 +18,9 @@ export class UsergroupsService extends BaseService { public async edit(id: ObjectId, obj: object) { try { - return await UsergroupsModel.update( - { _id: id }, obj, { runValidators: true, context: "query" } - ).exec(); + return await UsergroupsModel + .update({ _id: id }, obj, this.DEF_UPDATE_OPTIONS) + .exec(); } catch (error) { throw new BadRequestException(error.toString()); } diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index 99ca1fb..96aeb0a 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -78,9 +78,9 @@ export class UsersService extends BaseService { throw new BadRequestException("Empty Content"); } try { - return await UsersModel.update({ _id: id }, content, { - runValidators: true, context: "query" - }).exec(); + return await UsersModel + .update({ _id: id }, content, this.DEF_UPDATE_OPTIONS) + .exec(); } catch (error) { throw new BadRequestException(error.toString()); } diff --git a/test/helpers/database/auth.ts b/test/helpers/database/auth.ts index 0e85513..23c0d2e 100644 --- a/test/helpers/database/auth.ts +++ b/test/helpers/database/auth.ts @@ -1,5 +1,5 @@ import supertest = require("supertest"); -import { newUser } from "./user"; +import { newUser } from "@db/user"; import { newName } from "../utils"; export const login = async ( diff --git a/test/helpers/database/user.ts b/test/helpers/database/user.ts index d45dac0..c7d0fee 100644 --- a/test/helpers/database/user.ts +++ b/test/helpers/database/user.ts @@ -4,6 +4,15 @@ import { UsersService } from "@services/users"; import { SystemService } from "@services/system"; import { newName } from "../utils"; +let usersSvr: UsersService; + +const init = () => { + if (!usersSvr) { + usersSvr = new UsersService(new SystemService()); + } + return usersSvr; +}; + export const newUser = (username?: string, password?: string) => { return newUserWithUsergroup(username, password); }; @@ -11,7 +20,8 @@ export const newUser = (username?: string, password?: string) => { export const newUserWithUsergroup = ( username = newName(), password = newName(), gid?: ObjectId ) => { - return new UsersService(new SystemService()).addUser({ + init(); + return usersSvr.addUser({ username, password }, gid); }; From 0b8ab4e807f269135bff477ad4262dea243de5a7 Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 14 Mar 2018 10:08:09 +0800 Subject: [PATCH 26/49] Fix Validator always fail --- src/models/Collection.ts | 2 +- src/models/Good.ts | 4 ++-- src/models/Regexp.ts | 6 +++--- src/models/Token.ts | 2 +- src/models/User.ts | 6 +++--- src/models/Usergroup.ts | 2 +- src/models/common.ts | 8 ++++---- test/models/regexp.spec.ts | 6 ++++-- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/models/Collection.ts b/src/models/Collection.ts index 983afa7..3309ff2 100644 --- a/src/models/Collection.ts +++ b/src/models/Collection.ts @@ -48,7 +48,7 @@ const CollectionsSchema = new Base(Definition).createSchema(); CollectionsSchema.path("name").validate({ isAsync: true, validator: async function nameValidator(val, respond) { - respond(await existsValidator(Model, "name", val)); + respond(await existsValidator.bind(this)(Model, "name", val)); }, message: "The name is existed" }); diff --git a/src/models/Good.ts b/src/models/Good.ts index 9c2d8f3..bd96b28 100644 --- a/src/models/Good.ts +++ b/src/models/Good.ts @@ -64,7 +64,7 @@ const GoodsSchema = new Base(Definition).createSchema(); GoodsSchema.path("md5sum").validate({ isAsync: true, validator: async function md5ExistsValidator(val, respond) { - respond(await existsValidator(Model, "md5sum", val, { + respond(await existsValidator.bind(this)(Model, "md5sum", val, { update: false })); }, @@ -74,7 +74,7 @@ GoodsSchema.path("md5sum").validate({ GoodsSchema.path("sha256sum").validate({ isAsync: true, validator: async function sha256ExistsValidator(val, respond) { - respond(await existsValidator(Model, "sha256sum", val, { + respond(await existsValidator.bind(this)(Model, "sha256sum", val, { update: false })); }, diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index 30de163..0bb5ef2 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -125,9 +125,9 @@ RegexpSchema.path("hidden").validate({ if (!value) { // hidden === false return respond(true); } - respond(await existsValidator(Model, "hidden", value, { - extraCond: { value: this.value } - })); + respond(await existsValidator.bind(this)( + Model, "hidden", value, { extraCond: { value: this.value } } + )); }, message: "Only one active item with every value" }); diff --git a/src/models/Token.ts b/src/models/Token.ts index e0ab0ca..17bd2bb 100644 --- a/src/models/Token.ts +++ b/src/models/Token.ts @@ -36,7 +36,7 @@ const TokensSchema = new Base(Definition).createSchema(); TokensSchema.path("token").validate({ isAsync: true, validator: async (val, respond) => { - respond(await existsValidator(Model, "token", val, { + respond(await existsValidator.bind(this)(Model, "token", val, { update: false })); }, diff --git a/src/models/User.ts b/src/models/User.ts index dfe2123..4206ecd 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -48,9 +48,9 @@ UsersSchema.path("username").validate({ if (this && !this.isNew) { return respond(true); } - respond( - await existsValidator(Model, "username", val, { update: false }) - ); + respond(await existsValidator.bind(this)( + Model, "username", val, { update: false } + )); }, message: "The username is existed" }); diff --git a/src/models/Usergroup.ts b/src/models/Usergroup.ts index 608ee8a..d5880eb 100644 --- a/src/models/Usergroup.ts +++ b/src/models/Usergroup.ts @@ -20,7 +20,7 @@ const UsergroupsSchema = new Base(Definition).createSchema(); UsergroupsSchema.path("name").validate({ isAsync: true, validator: async function nameExistValidator(val, respond) { - return respond(await existsValidator(Model, "name", val)); + return respond(await existsValidator.bind(this)(Model, "name", val)); }, message: "The Name is exist" }); diff --git a/src/models/common.ts b/src/models/common.ts index d874fb9..8604b52 100644 --- a/src/models/common.ts +++ b/src/models/common.ts @@ -57,14 +57,14 @@ interface IExistsValidatorOptions { extraCond?: object; } -const existsValidatorOptions = { +const existsValidatorOptions: IExistsValidatorOptions = { update: true }; -export async function existsValidator( +export const existsValidator = async function ExistsValidatorFn( model: M, field: string, value, opts?: IExistsValidatorOptions ) { - const options = Object.assign({ }, existsValidator, opts); + const options = Object.assign({ }, existsValidatorOptions, opts); if (options.update && this && !this.isNew) { const id = this.getQuery()._id; const col = await model.findById(id).exec(); @@ -76,4 +76,4 @@ export async function existsValidator( Object.assign({ }, (options.extraCond || { }), { [field]: value }); const result = await model.findOne(cond).exec(); return !result; -} +}; diff --git a/test/models/regexp.spec.ts b/test/models/regexp.spec.ts index a2ecd1c..2da0ff4 100644 --- a/test/models/regexp.spec.ts +++ b/test/models/regexp.spec.ts @@ -28,9 +28,11 @@ describe("RegExp Model", () => { return db.drop(ids); }); + const REG = new RegExp(newName()); + it("Add Regexp", async () => { const md5sum = md5(Date.now() + ""); - const reg = await RegexpsModel.addRegexp(md5sum, /[\da-fA-F]/.source); + const reg = await RegexpsModel.addRegexp(md5sum, REG.source); ids.regexps.push(reg._id); reg.should.be.not.an.empty(); }); @@ -39,7 +41,7 @@ describe("RegExp Model", () => { const md5sum = md5(Date.now() + ""); let reg: RegexpDoc; - reg = await RegexpsModel.addRegexp(md5sum, /[\da-fA-F]/.source); + reg = await RegexpsModel.addRegexp(md5sum, REG.source); ids.regexps.push(reg._id); reg = await RegexpsModel.removeRegexp(reg._id); reg = await RegexpsModel.findById(reg._id).exec(); From 5da6ae14bf6822055e5589b47727623340d6ab4f Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 14 Mar 2018 11:05:02 +0800 Subject: [PATCH 27/49] Add Cors Middleware --- src/modules/common/middlewares/cors.middleware.ts | 11 +++++++++++ src/modules/controllers.module.ts | 7 +++++++ 2 files changed, 18 insertions(+) create mode 100644 src/modules/common/middlewares/cors.middleware.ts diff --git a/src/modules/common/middlewares/cors.middleware.ts b/src/modules/common/middlewares/cors.middleware.ts new file mode 100644 index 0000000..b96e53f --- /dev/null +++ b/src/modules/common/middlewares/cors.middleware.ts @@ -0,0 +1,11 @@ +import { Middleware, NestMiddleware, ExpressMiddleware } from "@nestjs/common"; +import * as cors from "cors"; + +@Middleware() +export class CorsMiddleware implements NestMiddleware { + public resolve(...args: any[]): ExpressMiddleware { + return (req, res, next) => { + return cors(); + }; + } +} diff --git a/src/modules/controllers.module.ts b/src/modules/controllers.module.ts index b383edd..2488722 100644 --- a/src/modules/controllers.module.ts +++ b/src/modules/controllers.module.ts @@ -28,6 +28,7 @@ import { RolesMiddleware } from "./common/middlewares/roles.middleware"; import { ReloadSessionMiddleware } from "./common/middlewares/reloadSession.middleware"; +import { CorsMiddleware } from "./common/middlewares/cors.middleware"; // endregion Middlewares // region Services @@ -76,6 +77,12 @@ export class ControllersModule { .apply(UploadFileMiddleware).forRoutes(this.uploadFileMethod) .apply(UploadFilesMiddleware).forRoutes(this.uploadFilesMethod) // endregion Upload + // region Cors + .apply(CorsMiddleware).forRoutes([ + GoodsController, CollectionsController, GoodsController, + AuthAdminController + ]) + // endregion Cors // region Log .apply([ ApiLoggerMiddleware ]) .forRoutes(...controllers.filter((item) => { From 35499ca6ce79a10933b2debf48e46c48e56191fa Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 14 Mar 2018 11:30:40 +0800 Subject: [PATCH 28/49] User Nest's Cors --- src/index.ts | 4 +++- src/modules/common/middlewares/cors.middleware.ts | 11 ----------- src/modules/controllers.module.ts | 7 ------- 3 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 src/modules/common/middlewares/cors.middleware.ts diff --git a/src/index.ts b/src/index.ts index 8b085f1..4c9d6c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,9 @@ import { isDevelopment } from "./modules/common/helper/env"; const bootstrap = async () => { const server = initExpress(); - const app = await NestFactory.create(ApplicationModule, server, { }); + const app = await NestFactory.create(ApplicationModule, server, { + cors: true + }); app.useGlobalPipes(new ValidationPipe()); if (isDevelopment) { diff --git a/src/modules/common/middlewares/cors.middleware.ts b/src/modules/common/middlewares/cors.middleware.ts deleted file mode 100644 index b96e53f..0000000 --- a/src/modules/common/middlewares/cors.middleware.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Middleware, NestMiddleware, ExpressMiddleware } from "@nestjs/common"; -import * as cors from "cors"; - -@Middleware() -export class CorsMiddleware implements NestMiddleware { - public resolve(...args: any[]): ExpressMiddleware { - return (req, res, next) => { - return cors(); - }; - } -} diff --git a/src/modules/controllers.module.ts b/src/modules/controllers.module.ts index 2488722..b383edd 100644 --- a/src/modules/controllers.module.ts +++ b/src/modules/controllers.module.ts @@ -28,7 +28,6 @@ import { RolesMiddleware } from "./common/middlewares/roles.middleware"; import { ReloadSessionMiddleware } from "./common/middlewares/reloadSession.middleware"; -import { CorsMiddleware } from "./common/middlewares/cors.middleware"; // endregion Middlewares // region Services @@ -77,12 +76,6 @@ export class ControllersModule { .apply(UploadFileMiddleware).forRoutes(this.uploadFileMethod) .apply(UploadFilesMiddleware).forRoutes(this.uploadFilesMethod) // endregion Upload - // region Cors - .apply(CorsMiddleware).forRoutes([ - GoodsController, CollectionsController, GoodsController, - AuthAdminController - ]) - // endregion Cors // region Log .apply([ ApiLoggerMiddleware ]) .forRoutes(...controllers.filter((item) => { From 949343d7099ab5fd7ac0411c3aba67ac709f35e4 Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 14 Mar 2018 15:41:13 +0800 Subject: [PATCH 29/49] About Gitlab Issue 2 --- src/modules/regexps/regexps.controller.ts | 14 +--- test/api/regexps.e2e.ts | 2 +- test/issues/gitlab/issue_2.e2e.ts | 88 +++++++++++++++++++++++ 3 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 test/issues/gitlab/issue_2.e2e.ts diff --git a/src/modules/regexps/regexps.controller.ts b/src/modules/regexps/regexps.controller.ts index a418ef4..84db6c6 100644 --- a/src/modules/regexps/regexps.controller.ts +++ b/src/modules/regexps/regexps.controller.ts @@ -72,19 +72,9 @@ export class RegexpsAdminController { @ApiOperation({ title: "Edit RegExp" }) // endregion Swagger Docs public async edit(@Body() ctx: EditRegexpDot, @Param() param: RidDto) { - const data: EditRegexpRawDot = { }; - if (ctx.name) { data.name = ctx.name; } - if (ctx.value) { data.value = ctx.value; } - if (ctx.link) { data.link = ctx.link; } - if (ctx.hidden !== null && ctx.hidden !== undefined) { - data.hidden = ctx.hidden; - } - if (Object.keys(data).length === 0) { - throw new BadRequestException("No Params"); - } - const regexp = await this.regexpsSvr.editById(param.rid, data); + const regexp = await this.regexpsSvr.editById(param.rid, ctx); if (!regexp) { - throw new BadRequestException("NonExist RegExp"); + throw new BadRequestException("Non Exist RegExp"); } return new DefResDto(); } diff --git a/test/api/regexps.e2e.ts b/test/api/regexps.e2e.ts index 514b0d4..59e9751 100644 --- a/test/api/regexps.e2e.ts +++ b/test/api/regexps.e2e.ts @@ -177,7 +177,7 @@ describe("Regexp E2E Api", () => { status.should.be.eql(400); }); - step("Modify with Empty Param", async () => { + xstep("Modify with Empty Param", async () => { const raw = await RegexpsModel.addRegexp( newName(), "^empty.param" ); diff --git a/test/issues/gitlab/issue_2.e2e.ts b/test/issues/gitlab/issue_2.e2e.ts new file mode 100644 index 0000000..59c180f --- /dev/null +++ b/test/issues/gitlab/issue_2.e2e.ts @@ -0,0 +1,88 @@ +import * as supertest from "supertest"; +import categories = require("@db/categories"); +import auth = require("@db/auth"); +import * as db from "../../helpers/database"; +import * as server from "../../helpers/server"; +import { newName } from "../../helpers/utils"; + +/** + * Fix [Issue 2](http://git.pbr.link/BoxSystem/StoreBox/issues/2) + */ +describe("Fix Issues", () => { + + let request: supertest.SuperTest; + + before(() => { + return db.connect(); + }); + + const ids = { + users: [ ], + categories: [ ], + regexps: [ ] + }; + + after(() => { + return db.drop(ids); + }); + + before(async () => { + request = await server.init(); + }); + + describe("Gitlab 2 [Cant Modify Regexp link]", () => { + + before("login", async () => { + ids.users.push((await auth.login(request))[0]); + }); + + let cids = [ ]; + step("Add Category group", async () => { + cids = await categories.addCategories(); + ids.categories.push(...cids); + }); + + step("Add One Category with Regexp", async () => { + const docs = await db.addCategoryAndRegexp( + new RegExp(newName()) + ); + ids.categories.push(docs[0]._id); + ids.regexps.push(docs[1]._id); + }); + + step("Modify Regexp link", async () => { + const url = `/api/v1/regexps/${ids.regexps[0]}`; + const targetId = ids.categories[10].toString(); + + const { status } = await request.post(url) + .send({ + link: targetId + }).then(); + status.should.be.not.eql(400); + + const { body } = await request.get(url).then(); + body.link._id.should.be.eql(targetId); + }); + + step("Delete Category", () => { + const targetId = ids.categories[10].toString(); + const url = `/api/v1/categories/${targetId}`; + return request.delete(url).then(); + }); + + step("Modify Regexp link", async () => { + const url = `/api/v1/regexps/${ids.regexps[0]}`; + const targetId = ids.categories[8].toString(); + + const { status } = await request.post(url) + .send({ + link: targetId + }).then(); + status.should.be.not.eql(400); + + const { body } = await request.get(url).then(); + body.link._id.should.be.eql(targetId); + }); + + }); +}); From 3d22b8d4d27df7cd43f4d181b0e5ae101b847189 Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 14 Mar 2018 15:49:39 +0800 Subject: [PATCH 30/49] Move Github Issues Test Units --- test/issues/{ => github}/github_issue_16.e2e.ts | 4 ++-- test/issues/{ => github}/github_issue_21.e2e.ts | 6 +++--- test/issues/{ => github}/github_issue_22.e2e.ts | 6 +++--- test/issues/{ => github}/github_issue_27.e2e.ts | 6 +++--- test/issues/{ => github}/github_issue_28.e2e.ts | 4 ++-- test/issues/{ => github}/github_issue_30.spec.ts | 4 ++-- test/issues/{ => github}/github_issue_31.e2e.ts | 11 +++++------ test/issues/{ => github}/github_issue_35.e2e.ts | 6 +++--- 8 files changed, 23 insertions(+), 24 deletions(-) rename test/issues/{ => github}/github_issue_16.e2e.ts (95%) rename test/issues/{ => github}/github_issue_21.e2e.ts (94%) rename test/issues/{ => github}/github_issue_22.e2e.ts (92%) rename test/issues/{ => github}/github_issue_27.e2e.ts (91%) rename test/issues/{ => github}/github_issue_28.e2e.ts (92%) rename test/issues/{ => github}/github_issue_30.spec.ts (90%) rename test/issues/{ => github}/github_issue_31.e2e.ts (87%) rename test/issues/{ => github}/github_issue_35.e2e.ts (90%) diff --git a/test/issues/github_issue_16.e2e.ts b/test/issues/github/github_issue_16.e2e.ts similarity index 95% rename from test/issues/github_issue_16.e2e.ts rename to test/issues/github/github_issue_16.e2e.ts index fc3e4e4..ff5c9a1 100644 --- a/test/issues/github_issue_16.e2e.ts +++ b/test/issues/github/github_issue_16.e2e.ts @@ -2,8 +2,8 @@ import supertest = require("supertest"); import { connect, drop, newUser, addCategoryAndRegexp -} from "../helpers/database"; -import { init } from "../helpers/server"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; import auth = require("@db/auth"); /** diff --git a/test/issues/github_issue_21.e2e.ts b/test/issues/github/github_issue_21.e2e.ts similarity index 94% rename from test/issues/github_issue_21.e2e.ts rename to test/issues/github/github_issue_21.e2e.ts index 76fccdb..7934d27 100644 --- a/test/issues/github_issue_21.e2e.ts +++ b/test/issues/github/github_issue_21.e2e.ts @@ -2,10 +2,10 @@ import supertest = require("supertest"); import { connect, drop, newUser, addCategoryAndRegexp -} from "../helpers/database"; -import { init } from "../helpers/server"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../helpers/utils"; +import { newName } from "../../helpers/utils"; /** * About [Issue 21](https://github.com/Arylo/StoreBox/issues/21) diff --git a/test/issues/github_issue_22.e2e.ts b/test/issues/github/github_issue_22.e2e.ts similarity index 92% rename from test/issues/github_issue_22.e2e.ts rename to test/issues/github/github_issue_22.e2e.ts index 710fc57..d4e3ba2 100644 --- a/test/issues/github_issue_22.e2e.ts +++ b/test/issues/github/github_issue_22.e2e.ts @@ -2,10 +2,10 @@ import supertest = require("supertest"); import { connect, drop, addCategoryAndRegexp -} from "../helpers/database"; -import { init } from "../helpers/server"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../helpers/utils"; +import { newName } from "../../helpers/utils"; /** * Fix [Issue 22](https://github.com/Arylo/StoreBox/issues/22) diff --git a/test/issues/github_issue_27.e2e.ts b/test/issues/github/github_issue_27.e2e.ts similarity index 91% rename from test/issues/github_issue_27.e2e.ts rename to test/issues/github/github_issue_27.e2e.ts index 0f5c401..59f37c8 100644 --- a/test/issues/github_issue_27.e2e.ts +++ b/test/issues/github/github_issue_27.e2e.ts @@ -2,10 +2,10 @@ import supertest = require("supertest"); import { connect, drop, newCategory -} from "../helpers/database"; -import { init } from "../helpers/server"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../helpers/utils"; +import { newName } from "../../helpers/utils"; /** * Fix [Issue 27](https://github.com/Arylo/StoreBox/issues/27) diff --git a/test/issues/github_issue_28.e2e.ts b/test/issues/github/github_issue_28.e2e.ts similarity index 92% rename from test/issues/github_issue_28.e2e.ts rename to test/issues/github/github_issue_28.e2e.ts index 4530b0d..e509c04 100644 --- a/test/issues/github_issue_28.e2e.ts +++ b/test/issues/github/github_issue_28.e2e.ts @@ -2,8 +2,8 @@ import supertest = require("supertest"); import { connect, drop, newUser -} from "../helpers/database"; -import { init } from "../helpers/server"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; import auth = require("@db/auth"); /** diff --git a/test/issues/github_issue_30.spec.ts b/test/issues/github/github_issue_30.spec.ts similarity index 90% rename from test/issues/github_issue_30.spec.ts rename to test/issues/github/github_issue_30.spec.ts index 605bf3f..52459d5 100644 --- a/test/issues/github_issue_30.spec.ts +++ b/test/issues/github/github_issue_30.spec.ts @@ -1,7 +1,7 @@ import isRegExp = require("@utils/isRegExp"); -import { connect } from "../helpers/database"; +import { connect } from "../../helpers/database"; import { Model as RegexpsModel } from "@models/Regexp"; -import { newName } from "../helpers/utils"; +import { newName } from "../../helpers/utils"; /** * Fix [Issue 30](https://github.com/Arylo/StoreBox/issues/30) diff --git a/test/issues/github_issue_31.e2e.ts b/test/issues/github/github_issue_31.e2e.ts similarity index 87% rename from test/issues/github_issue_31.e2e.ts rename to test/issues/github/github_issue_31.e2e.ts index 1a7997a..b690e5f 100644 --- a/test/issues/github_issue_31.e2e.ts +++ b/test/issues/github/github_issue_31.e2e.ts @@ -5,13 +5,12 @@ import { basename } from "path"; import { Model as GoodsModels } from "@models/Good"; import { connect, drop, newUser, addCategoryAndRegexp -} from "../helpers/database"; -import { init } from "../helpers/server"; -import { uploadFile } from "../helpers/files"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; +import * as files from "../../helpers/files"; import { config } from "@utils/config"; import auth = require("@db/auth"); -import files = require("../helpers/files"); /** * Fix [Issue 31](https://github.com/Arylo/StoreBox/issues/31) @@ -72,14 +71,14 @@ describe("Fix Issues", () => { step("Upload Success", async () => { const { body: result, status - } = await uploadFile(request, filepath); + } = await files.uploadFile(request, filepath); status.should.be.eql(201); }); step("Upload Fail", async () => { const { body: result, status - } = await uploadFile(request, filepath); + } = await files.uploadFile(request, filepath); status.should.be.not.eql(201); status.should.be.eql(400); }); diff --git a/test/issues/github_issue_35.e2e.ts b/test/issues/github/github_issue_35.e2e.ts similarity index 90% rename from test/issues/github_issue_35.e2e.ts rename to test/issues/github/github_issue_35.e2e.ts index 1280e3f..79254d7 100644 --- a/test/issues/github_issue_35.e2e.ts +++ b/test/issues/github/github_issue_35.e2e.ts @@ -2,10 +2,10 @@ import supertest = require("supertest"); import { connect, drop, newUser -} from "../helpers/database"; -import { init } from "../helpers/server"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../helpers/utils"; +import { newName } from "../../helpers/utils"; /** * The Feature of Edit User From a99552762dbf13758da4b9d6dbecac1a4a5b617a Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 14 Mar 2018 16:01:23 +0800 Subject: [PATCH 31/49] Lost `category` field in `/goods` --- src/modules/files/goods.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/files/goods.controller.ts b/src/modules/files/goods.controller.ts index 2fbedcf..67b3914 100644 --- a/src/modules/files/goods.controller.ts +++ b/src/modules/files/goods.controller.ts @@ -49,7 +49,7 @@ export class GoodsController { .map((doc) => { const good = doc.toObject() as IGoodsRaw; const category = categories[good.category.toString()]; - delete good.category; + // delete good.category; good.uploader = good.uploader.nickname as any; good.tags = Array.from(new Set(good.tags.concat(category.tags))); From 2b0fc7e2f94a8545434575595c911062df88508a Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 14 Mar 2018 16:11:51 +0800 Subject: [PATCH 32/49] Update Api Doc --- src/modules/files/goods.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/files/goods.dto.ts b/src/modules/files/goods.dto.ts index 7291581..3792155 100644 --- a/src/modules/files/goods.dto.ts +++ b/src/modules/files/goods.dto.ts @@ -3,7 +3,7 @@ import { ApiModelPropertyOptional, ApiModelProperty } from "@nestjs/swagger"; import { PerPageDto } from "@dtos/page"; export class GoodsQueryDto extends PerPageDto { - @ApiModelPropertyOptional({ type: String }) + @ApiModelPropertyOptional({ type: String, isArray: true }) @IsString({ each: true }) From 1431c66bbcd884637524f420328bd17dd179c6b7 Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 15 Mar 2018 10:57:24 +0800 Subject: [PATCH 33/49] Add Feature ## Add - Move Good to Other Category - Remove Good --- src/modules/common/services/goods.service.ts | 46 ++++++- src/modules/files/files.controller.ts | 3 +- src/modules/goods/goods.controller.ts | 33 ++++- src/modules/goods/goods.dto.ts | 19 ++- test/issues/gitlab/issue_1.e2e.ts | 125 +++++++++++++++++++ 5 files changed, 218 insertions(+), 8 deletions(-) create mode 100644 test/issues/gitlab/issue_1.e2e.ts diff --git a/src/modules/common/services/goods.service.ts b/src/modules/common/services/goods.service.ts index f1cf720..9514212 100644 --- a/src/modules/common/services/goods.service.ts +++ b/src/modules/common/services/goods.service.ts @@ -1,8 +1,12 @@ import { Component, BadRequestException } from "@nestjs/common"; -import { Model as GoodsModels } from "@models/Good"; import { ObjectId } from "@models/common"; +import { Model as GoodsModels, IGoods, IGoodsRaw } from "@models/Good"; +import { ICategory } from "@models/Categroy"; +import { Model as ValuesModel } from "@models/Value"; import { BaseService, IGetOptions } from "@services/base"; -import { isArray } from "util"; +import { isArray, isObject, isString } from "util"; +import { config } from "@utils/config"; +import fs = require("fs-extra"); @Component() export class GoodsService extends BaseService { @@ -134,7 +138,9 @@ export class GoodsService extends BaseService { */ public async editById(id: ObjectId, ctx: object) { try { - await GoodsModels.findByIdAndUpdate(id, ctx).exec(); + await GoodsModels + .update({ _id: id }, ctx, this.DEF_UPDATE_OPTIONS) + .exec(); } catch (error) { throw new BadRequestException(error.toString()); } @@ -144,4 +150,38 @@ export class GoodsService extends BaseService { return GoodsModels.create(ctx); } + public async remove(id: ObjectId) { + const doc = await this.getById(id); + if (!doc) { + throw new BadRequestException("Non Exist Good ID"); + } + const good = doc.toObject(); + try { + await GoodsModels.findByIdAndRemove(id).exec(); + if (good.attributes.length > 0) { + const cond = (good.attributes as ObjectId[]) + .reduce((obj, item) => { + obj.$or.push({ _id: item }); + return obj; + }, { $or: [ ] }); + await ValuesModel.remove(cond).exec(); + } + } catch (error) { + throw new BadRequestException(error.toString()); + } + await fs.remove(this.getFilepath(good)); + } + + public getFilepath(good: IGoods) { + const filename = good.filename; + const cid = + isObject(good.category) && (good.category as ICategory)._id ? + (good.category as ICategory)._id : + good.category; + if (!cid) { + throw new BadRequestException(); + } + return `${config.paths.upload}/${cid}/${filename}`; + } + } diff --git a/src/modules/files/files.controller.ts b/src/modules/files/files.controller.ts index ff54425..e9aeb32 100644 --- a/src/modules/files/files.controller.ts +++ b/src/modules/files/files.controller.ts @@ -51,8 +51,7 @@ export class FilesController { } const good = obj.toObject(); - const filepath = - `${config.paths.upload}/${params.cid}/${good.filename}`; + const filepath = this.goodsSvr.getFilepath(good); if (!good.active) { throw new BadRequestException("Disallow download the File"); diff --git a/src/modules/goods/goods.controller.ts b/src/modules/goods/goods.controller.ts index ce80301..d4a6cde 100644 --- a/src/modules/goods/goods.controller.ts +++ b/src/modules/goods/goods.controller.ts @@ -1,6 +1,6 @@ import { Controller, Req, Res, Body, Get, Post, Param, Session, - HttpStatus, BadRequestException, UseGuards, Delete, HttpCode, Query + HttpStatus, BadRequestException, UseGuards, Delete, HttpCode, Query, Put } from "@nestjs/common"; import { ApiBearerAuth, ApiUseTags, ApiResponse, ApiOperation, ApiImplicitParam, @@ -32,7 +32,7 @@ import multer = require("multer"); import { isArray } from "util"; import { CreateValueDto, EditValueDto } from "../values/values.dto"; -import { GoodAttributeParamDto, UploadQueryDto } from "./goods.dto"; +import { GoodAttributeParamDto, UploadQueryDto, EditBodyDto } from "./goods.dto"; @UseGuards(RolesGuard) @Controller("api/v1/goods") @@ -347,4 +347,33 @@ export class GoodsAdminController { return new DefResDto(); } + @Roles("admin") + @Put("/:gid") + @Delete("/:gid") + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Modify Good" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Modify Success" + }) + // endregion Swagger Docs + public async edit(@Param() param: GidDto, @Body() body: EditBodyDto) { + await this.goodsSvr.editById(param.gid, body); + return new DefResDto(); + } + + @Roles("admin") + @Delete("/:gid") + // region Swagger Docs + @HttpCode(HttpStatus.OK) + @ApiOperation({ title: "Delete Good" }) + @ApiResponse({ + status: HttpStatus.OK, description: "Delete Success" + }) + // endregion Swagger Docs + public async delete(@Param() param: GidDto) { + await this.goodsSvr.remove(param.gid); + return new DefResDto(); + } + } diff --git a/src/modules/goods/goods.dto.ts b/src/modules/goods/goods.dto.ts index 84b9897..6d8b18d 100644 --- a/src/modules/goods/goods.dto.ts +++ b/src/modules/goods/goods.dto.ts @@ -1,5 +1,5 @@ import { IGidDto, IAidDto } from "@dtos/ids"; -import { ApiModelProperty } from "@nestjs/swagger"; +import { ApiModelProperty, ApiModelPropertyOptional } from "@nestjs/swagger"; import { IsMongoId, IsString, IsOptional } from "class-validator"; import { ObjectId } from "@models/common"; import { IGoods } from "@models/Good"; @@ -34,3 +34,20 @@ export class UploadQueryDto { @IsOptional() public readonly append?: string[]; } + +export class EditBodyDto { + @ApiModelPropertyOptional({ type: String, description: "Good Name" }) + @IsOptional() + public readonly name?: string; + @ApiModelPropertyOptional({ type: Boolean }) + @IsOptional() + public readonly hidden?: boolean; + @ApiModelPropertyOptional({ type: String, description: "Category ID" }) + @IsOptional() + public readonly category?: ObjectId; + @ApiModelPropertyOptional({ + type: String, description: "Filename when download" + }) + @IsOptional() + public readonly originname?: string; +} diff --git a/test/issues/gitlab/issue_1.e2e.ts b/test/issues/gitlab/issue_1.e2e.ts new file mode 100644 index 0000000..9ce4e46 --- /dev/null +++ b/test/issues/gitlab/issue_1.e2e.ts @@ -0,0 +1,125 @@ +import * as supertest from "supertest"; +import categories = require("@db/categories"); +import auth = require("@db/auth"); +import * as db from "../../helpers/database"; +import * as server from "../../helpers/server"; +import { newName } from "../../helpers/utils"; +import * as files from "../../helpers/files"; +import goods = require("@db/goods"); + +/** + * Fix [Issue 1](http://git.pbr.link/BoxSystem/StoreBox/issues/1) + */ +describe("Fix Issues", () => { + + let request: supertest.SuperTest; + + before(() => { + return db.connect(); + }); + + const ids = { + users: [ ], + categories: [ ], + regexps: [ ], + goods: [ ] + }; + + after(() => { + return db.drop(ids); + }); + + before(async () => { + request = await server.init(); + }); + + const filepaths = [ ]; + after(() => { + return files.remove(filepaths); + }); + + before("login", async () => { + ids.users.push((await auth.login(request))[0]); + }); + + let cids = [ ]; + step("Add Category group", async () => { + cids = await categories.addCategories(); + ids.categories.push(...cids); + }); + + describe("Gitlab 1 [Delete Good]", () => { + + const filename = newName(); + let cid; + + step("Add One Category with Regexp", async () => { + const docs = await db.addCategoryAndRegexp( + new RegExp(filename) + ); + cid = docs[0]._id; + ids.categories.push(cid); + ids.regexps.push(docs[1]._id); + }); + + let gid; + + step("Upload File", async () => { + const filepath = await files.newFile(filename); + filepaths.push(filepath); + await files.uploadFile(request, filepath); + gid = await goods.getIdByOriginname(filename); + ids.goods.push(gid); + }); + + step("Delete Good", async () => { + const url = `/api/v1/goods/${gid}`; + const { status } = await request.delete(url).then(); + status.should.be.eql(200); + }); + + step("404 for Good", async () => { + const url = `/files/categories/${cid}/goods/${gid}`; + const { status } = await request.get(url).then(); + status.should.be.eql(404); + }); + + }); + + describe("Gitlab 1 [Move Good to Other Category]", () => { + + const filename = newName(); + + step("Add One Category with Regexp", async () => { + const docs = await db.addCategoryAndRegexp( + new RegExp(filename) + ); + ids.categories.push(docs[0]._id); + ids.regexps.push(docs[1]._id); + }); + + let gid; + + step("Upload File", async () => { + const filepath = await files.newFile(filename); + filepaths.push(filepath); + await files.uploadFile(request, filepath); + gid = await goods.getIdByOriginname(filename); + ids.goods.push(gid); + }); + + step("Move Good ", async () => { + const url = `/api/v1/goods/${gid}`; + const cid = ids.categories[5].toString(); + const { status } = await request.put(url) + .send({ category: cid }) + .then(); + status.should.be.eql(200); + + const { body } = await request.get(url).then(); + body.category._id.toString().should.be.eql(cid); + }); + + }); + +}); From e340c0bf57da2c81dbfbb7dc006c84ace7dc4ca5 Mon Sep 17 00:00:00 2001 From: Arylo Date: Thu, 15 Mar 2018 14:01:46 +0800 Subject: [PATCH 34/49] Fix Edit Regexp Fail --- src/models/Regexp.ts | 15 ++++++++------- src/modules/regexps/regexps.dto.ts | 4 ++-- test/api/regexps.e2e.ts | 6 +++--- test/issues/gitlab/issue_2.e2e.ts | 12 ++++++++++++ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index 0bb5ef2..7ef62d1 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -82,9 +82,10 @@ interface IRegexpModel extends M { // region Validators RegexpSchema.path("name").validate({ isAsync: true, - validator: async (value, respond) => { - const result = await Model.findOne({ name: value }).exec(); - return respond(!result); + validator: async function nameExistValidator(value, respond) { + return respond(await existsValidator.bind(this)( + Model, "name", value + )); }, message: "The name is exist" }); @@ -99,13 +100,13 @@ RegexpSchema.path("value").validate({ RegexpSchema.path("value").validate({ isAsync: true, - validator: async function ValueExistValidator(value, respond) { + validator: async function valueExistValidator(value, respond) { if (this && this.hidden) { return respond(true); } - const result = - await Model.findOne({ value: value, hidden: false }).exec(); - return respond(!result); + return respond(await existsValidator.bind(this)( + Model, "value", value, { extraCond: { hidden: false } } + )); }, message: "The value is exist" }); diff --git a/src/modules/regexps/regexps.dto.ts b/src/modules/regexps/regexps.dto.ts index 43fb6bf..d758eeb 100644 --- a/src/modules/regexps/regexps.dto.ts +++ b/src/modules/regexps/regexps.dto.ts @@ -1,4 +1,4 @@ -import { IsString, IsMongoId, IsOptional } from "class-validator"; +import { IsString, IsMongoId, IsOptional, IsBoolean } from "class-validator"; import { ObjectId } from "@models/common"; import { ApiModelProperty, ApiModelPropertyOptional } from "@nestjs/swagger"; @@ -48,6 +48,6 @@ export class EditRegexpDot implements IRegexp { public readonly link: ObjectId; @ApiModelPropertyOptional({ type: Boolean }) @IsOptional() - @IsMongoId() + @IsBoolean() public readonly hidden: boolean; } diff --git a/test/api/regexps.e2e.ts b/test/api/regexps.e2e.ts index 59e9751..a7cc000 100644 --- a/test/api/regexps.e2e.ts +++ b/test/api/regexps.e2e.ts @@ -164,15 +164,15 @@ describe("Regexp E2E Api", () => { status.should.be.eql(400); }); - step("Modify Exist Value", async () => { + // Ignore Self Value + xstep("Modify Exist Value", async () => { const data = { name: newName(), value: "^modify.exist.value" }; const raw = await RegexpsModel.addRegexp(data.name, data.value); ids.regexps.push(raw._id); - const { body: result, status: status } = - await request.post(`${URL}/${raw._id}`) + const { body: result, status } = await request.post(`${URL}/${raw._id}`) .send({ value: data.value }).then(); status.should.be.eql(400); }); diff --git a/test/issues/gitlab/issue_2.e2e.ts b/test/issues/gitlab/issue_2.e2e.ts index 59c180f..8f4d283 100644 --- a/test/issues/gitlab/issue_2.e2e.ts +++ b/test/issues/gitlab/issue_2.e2e.ts @@ -84,5 +84,17 @@ describe("Fix Issues", () => { body.link._id.should.be.eql(targetId); }); + step("Modify Regexp link with other fields", async () => { + const url = `/api/v1/regexps/${ids.regexps[0]}`; + const targetId = ids.categories[7].toString(); + + const { body } = await request.get(url).then(); + body.link = targetId; + + const { status, body: result } = await request.post(url) + .send(body).then(); + status.should.be.not.eql(400); + }); + }); }); From 7ad33c0ba497b47f5e4a9054e48d62d8f015101b Mon Sep 17 00:00:00 2001 From: Arylo Date: Fri, 16 Mar 2018 14:58:54 +0800 Subject: [PATCH 35/49] Fix Bug - Enabled the feature of Cache - Fix Collection Count always cache old value - Fix Test Fail sometime --- src/models/Collection.ts | 6 ++++++ .../categroies/categroies.controller.ts | 20 ++++++------------ src/modules/common/services/base.service.ts | 2 +- .../common/services/categories.service.ts | 21 ++++++++++++------- .../common/services/collections.service.ts | 10 ++++----- src/modules/common/services/goods.service.ts | 3 ++- .../common/services/regexps.service.ts | 8 +++---- test/api/goods_append.e2e.ts | 2 +- test/api/goods_specified_category.e2e.ts | 6 +++--- 9 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/models/Collection.ts b/src/models/Collection.ts index 3309ff2..370c775 100644 --- a/src/models/Collection.ts +++ b/src/models/Collection.ts @@ -76,4 +76,10 @@ CollectionsSchema.path("goods").validate({ }); // endregion validators +for (const method of MODIFY_MOTHODS) { + CollectionsSchema.post(method, () => { + cache.clear(); + }); +} + export const Model = model(FLAG, CollectionsSchema) as M; diff --git a/src/modules/categroies/categroies.controller.ts b/src/modules/categroies/categroies.controller.ts index 6c71d10..c95e34a 100644 --- a/src/modules/categroies/categroies.controller.ts +++ b/src/modules/categroies/categroies.controller.ts @@ -85,20 +85,12 @@ export class CategoriesAdminController { attrs.map((item) => item.toObject()._id) ); } - let result; - try { - result = await this.categoriesSvr.add({ - name: ctx.name, - tags: ctx.tags, - attributes: attrsIds, - pid: ctx.pid - }); - } catch (error) { - attrsIds.forEach((id) => { - ValuesModel.findByIdAndRemove(id).exec(); - }); - throw new BadRequestException(error.toString()); - } + const result = await this.categoriesSvr.add({ + name: ctx.name, + tags: ctx.tags, + attributes: attrsIds, + pid: ctx.pid + }); return result; } diff --git a/src/modules/common/services/base.service.ts b/src/modules/common/services/base.service.ts index fbcc3ca..01246de 100644 --- a/src/modules/common/services/base.service.ts +++ b/src/modules/common/services/base.service.ts @@ -17,7 +17,7 @@ export abstract class BaseService { private cache; protected setCache(cache) { - // this.cache = cache; + this.cache = cache; } protected loadAndCache( diff --git a/src/modules/common/services/categories.service.ts b/src/modules/common/services/categories.service.ts index ec929da..6c6a5c5 100644 --- a/src/modules/common/services/categories.service.ts +++ b/src/modules/common/services/categories.service.ts @@ -111,8 +111,12 @@ export class CategoriesService extends BaseService { return CategoriesModel.count({ }).exec(); } - public add(ctx: object) { - return CategoriesModel.create(ctx); + public async add(ctx: object) { + try { + return await CategoriesModel.create(ctx); + } catch (error) { + throw new BadRequestException(error.toString()); + } } public get(cond: object, opts?: IGetOptions) { @@ -121,15 +125,16 @@ export class CategoriesService extends BaseService { return p.exec(); } - public getById(id: ObjectId, opts?: IGetOptions) { - let p = CategoriesModel.findById(id); - p = this.documentQueryProcess(p, opts); - return p.exec(); + public async getById(id: ObjectId, opts?: IGetOptions) { + const arr = await this.get({ _id: id }, opts); + return arr.length === 0 ? null : arr[0]; } - public editById(id: ObjectId, ctx: object) { + public async editById(id: ObjectId, ctx: object) { try { - return CategoriesModel.findByIdAndUpdate(id, ctx).exec(); + return await CategoriesModel + .update({ _id: id }, ctx, this.DEF_UPDATE_OPTIONS) + .exec(); } catch (error) { throw new BadRequestException(error.toString()); } diff --git a/src/modules/common/services/collections.service.ts b/src/modules/common/services/collections.service.ts index 83ce11d..2591e47 100644 --- a/src/modules/common/services/collections.service.ts +++ b/src/modules/common/services/collections.service.ts @@ -36,7 +36,7 @@ export class CollectionsService extends BaseService { const perNum = pageObj.perNum; const page = pageObj.page; return this.loadAndCache( - `list_${uid.toString}_${perNum}_${page}`, + `list_${uid.toString()}_${perNum}_${page}`, () => { return CollectionsModel.find({ creator: uid }) .skip((page - 1) * perNum).limit(perNum) @@ -45,13 +45,13 @@ export class CollectionsService extends BaseService { .populate("goods") .exec(); }, - 7200 + 50 ); } public count(uid: ObjectId) { return this.loadAndCache( - `count_${uid}`, + `count_${uid.toString()}`, () => CollectionsModel.count({ creator: uid }).exec() ); } @@ -70,7 +70,7 @@ export class CollectionsService extends BaseService { return this.loadAndCache( `getByName_${name}`, () => p.exec(), - 7200 + 50 ); } @@ -84,7 +84,7 @@ export class CollectionsService extends BaseService { return this.loadAndCache( `getById_${id.toString()}`, () => p.exec(), - 7200 + 50 ); } diff --git a/src/modules/common/services/goods.service.ts b/src/modules/common/services/goods.service.ts index 9514212..c99c90b 100644 --- a/src/modules/common/services/goods.service.ts +++ b/src/modules/common/services/goods.service.ts @@ -37,7 +37,8 @@ export class GoodsService extends BaseService { .populate("uploader") .populate("attributes") .select("-category") - .exec() + .exec(), + 50 ); } diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index 3f4bd33..1bd47f5 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -121,7 +121,7 @@ export class RegexpsService extends BaseService { () => RegexpsModel.find({ }) .skip((page - 1) * perNum).limit(perNum) .populate("link").exec(), - 3000 + 50 ); } @@ -142,7 +142,7 @@ export class RegexpsService extends BaseService { return this.loadAndCache( FLAG, () => RegexpsModel.find(conditions).populate("link").exec(), - 3000 + 50 ); } else if (opts.appends && opts.appends.length > 0) { // 追加Categroy @@ -156,14 +156,14 @@ export class RegexpsService extends BaseService { return this.loadAndCache( FLAG, () => RegexpsModel.find(conditions).populate("link").exec(), - 3000 + 50 ); } else { const FLAG = "default_scan_regexps"; return this.loadAndCache( FLAG, () => RegexpsModel.find(DEF_CONDITIONS).populate("link").exec(), - 3000 + 50 ); } } diff --git a/test/api/goods_append.e2e.ts b/test/api/goods_append.e2e.ts index 7ee594a..a363100 100644 --- a/test/api/goods_append.e2e.ts +++ b/test/api/goods_append.e2e.ts @@ -75,7 +75,7 @@ describe("Upload Good with Append categories", () => { const { status, body: result } = await files.uploadFile( request, filepath, { query: { - "append": await categories.getNameById(targetId) + "append": encodeURI(await categories.getNameById(targetId)) }} ); status.should.be.eql(400); diff --git a/test/api/goods_specified_category.e2e.ts b/test/api/goods_specified_category.e2e.ts index 946e6c7..b56d6a4 100644 --- a/test/api/goods_specified_category.e2e.ts +++ b/test/api/goods_specified_category.e2e.ts @@ -81,7 +81,7 @@ describe("Upload Good with specified categories", () => { const { status } = await files.uploadFile( request, filepath, { query: { - "category": await categories.getNameById(targetId) + "category": encodeURI(await categories.getNameById(targetId)) }} ); status.should.be.eql(201); @@ -95,7 +95,7 @@ describe("Upload Good with specified categories", () => { const { status } = await files.uploadFile( request, filepath, { query: { - "category": await categories.getNameById(targetId) + "category": encodeURI(await categories.getNameById(targetId)) }} ); status.should.be.eql(201); @@ -109,7 +109,7 @@ describe("Upload Good with specified categories", () => { const { status } = await files.uploadFile( request, filepath, { query: { - "category": await categories.getNameById(targetId) + "category": encodeURI(await categories.getNameById(targetId)) }} ); status.should.be.eql(400); From 55293fd6669715b0e40f3ab95e4fedf6b9a95f36 Mon Sep 17 00:00:00 2001 From: Arylo Date: Fri, 16 Mar 2018 18:33:32 +0800 Subject: [PATCH 36/49] Add And Update ## Add - add regexp count check interceptor ## Update - more check when delete category --- .../common/decorators/route.decorator.ts | 8 --- .../regexp-count-check.interceptor.ts | 25 ++++++++ .../common/pipes/regexp-count-check.pipe.ts | 32 ----------- .../common/services/categories.service.ts | 57 +++++++++++-------- src/modules/goods/goods.controller.ts | 12 ++-- .../categories.e2e.ts} | 8 +-- test/api/{ => collections}/collections.e2e.ts | 10 ++-- .../collections_token.e2e.ts | 6 +- test/api/{ => goods}/goods.e2e.ts | 12 ++-- test/api/{ => goods}/goods_append.e2e.ts | 8 +-- .../goods_specified_category.e2e.ts | 8 +-- test/helpers/files.ts | 1 + test/tsconfig.json | 1 + tsconfig.json | 4 ++ 14 files changed, 99 insertions(+), 93 deletions(-) create mode 100644 src/modules/common/interceptors/regexp-count-check.interceptor.ts delete mode 100644 src/modules/common/pipes/regexp-count-check.pipe.ts rename test/api/{categroies.e2e.ts => categories/categories.e2e.ts} (91%) rename test/api/{ => collections}/collections.e2e.ts (97%) rename test/api/{ => collections}/collections_token.e2e.ts (95%) rename test/api/{ => goods}/goods.e2e.ts (84%) rename test/api/{ => goods}/goods_append.e2e.ts (93%) rename test/api/{ => goods}/goods_specified_category.e2e.ts (95%) diff --git a/src/modules/common/decorators/route.decorator.ts b/src/modules/common/decorators/route.decorator.ts index 33efe67..bed69fc 100644 --- a/src/modules/common/decorators/route.decorator.ts +++ b/src/modules/common/decorators/route.decorator.ts @@ -3,11 +3,3 @@ import { createRouteParamDecorator } from "@nestjs/common"; export const User = createRouteParamDecorator((data, req) => { return req.user; }); - -export const File = createRouteParamDecorator((data, req) => { - return req.file; -}); - -export const Files = createRouteParamDecorator((data, req) => { - return req.files; -}); diff --git a/src/modules/common/interceptors/regexp-count-check.interceptor.ts b/src/modules/common/interceptors/regexp-count-check.interceptor.ts new file mode 100644 index 0000000..36df5c4 --- /dev/null +++ b/src/modules/common/interceptors/regexp-count-check.interceptor.ts @@ -0,0 +1,25 @@ +import { Interceptor, NestInterceptor, BadRequestException } from "@nestjs/common"; +import { Observable } from "rxjs/Observable"; +import { RegexpsService } from "@services/regexps"; +import * as fs from "fs-extra"; + +@Interceptor() +export class RegexpCountCheckInterceptor implements NestInterceptor { + + constructor(private readonly regexpsSvr: RegexpsService) { } + + public async intercept( + dataOrRequest, context, stream$: Observable + ) { + const regexpCount = await this.regexpsSvr.count(); + if (regexpCount !== 0) { + return stream$; + } + const files = dataOrRequest.file ? + [ dataOrRequest.file ] : dataOrRequest.files; + for (const val of files) { + fs.remove(val.path); + } + throw new BadRequestException("Lost The Good Role"); + } +} diff --git a/src/modules/common/pipes/regexp-count-check.pipe.ts b/src/modules/common/pipes/regexp-count-check.pipe.ts deleted file mode 100644 index 785e002..0000000 --- a/src/modules/common/pipes/regexp-count-check.pipe.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - Pipe, PipeTransform, ArgumentMetadata, BadRequestException -} from "@nestjs/common"; -import { isArray } from "util"; -import fs = require("fs-extra"); -import { RegexpsService } from "@services/regexps"; - -type File = Express.Multer.File; - -@Pipe() -export class RegexpCountCheckPipe implements PipeTransform { - - private readonly regexpSvr = new RegexpsService(); - - public async transform(value: File | File[], metadata: ArgumentMetadata) { - const regexpCount = await this.regexpSvr.count(); - if (regexpCount !== 0) { - return value; - } - if (!value) { - return value; - } - if (isArray(value)) { - for (const val of value) { - fs.remove(val.path); - } - } else { - fs.remove(value.path); - } - throw new BadRequestException("Lost The Good Role"); - } -} diff --git a/src/modules/common/services/categories.service.ts b/src/modules/common/services/categories.service.ts index 6c6a5c5..4682356 100644 --- a/src/modules/common/services/categories.service.ts +++ b/src/modules/common/services/categories.service.ts @@ -4,6 +4,7 @@ import { Model as CategoriesModel, cache, ICategoryRaw, ICategory } from "@model import { isFunction, isArray } from "util"; import { BaseService, IGetOptions } from "@services/base"; import { difference } from "lodash"; +import { GoodsService } from "@services/goods"; interface IIdMap { [parentId: string]: ObjectId[]; @@ -12,35 +13,35 @@ interface IIdMap { @Component() export class CategoriesService extends BaseService { - constructor() { + constructor(private readonly goodsSvr: GoodsService) { super(); super.setCache(cache); } - private async getIdMap() { - // { parentId: childrenIds } - const map: IIdMap = { }; - const docs = await CategoriesModel.find().select("_id pid").exec(); - docs.forEach((doc) => { - const category = doc.toObject(); - let index; - if (!category.pid) { - index = "*"; - } else { - index = category.pid.toString(); - } - if (!map[index]) { - map[index] = [ ]; - } - map[index].push(category._id.toString()); + private getIdMap() { + return this.loadAndCache("IdMap", async () => { + // { parentId: childrenIds } + const map: IIdMap = { }; + const docs = await CategoriesModel.find().select("_id pid").exec(); + docs.forEach((doc) => { + const category = doc.toObject(); + let index; + if (!category.pid) { + index = "*"; + } else { + index = category.pid.toString(); + } + if (!map[index]) { + map[index] = [ ]; + } + map[index].push(category._id.toString()); + }); + return map; }); - return map; } public async getChildrenIds(pid: ObjectId) { - const map = await this.loadAndCache("IdMap", () => { - return this.getIdMap(); - }); + const map = await this.getIdMap(); const ids: ObjectId[] = [ ]; const childrenIds = map[pid.toString()]; if (childrenIds) { @@ -140,9 +141,19 @@ export class CategoriesService extends BaseService { } } - public removeById(id: ObjectId) { + public async removeById(id: ObjectId) { + const goods = await this.goodsSvr.getByCids(id); + if (goods.length > 0) { + throw new BadRequestException("Some Good in the category"); + } + const map = await this.getIdMap(); + if (map[id.toString()] && map[id.toString()].length > 0) { + throw new BadRequestException( + "The Category have some child categories" + ); + } try { - return CategoriesModel.findByIdAndRemove(id).exec(); + return await CategoriesModel.findByIdAndRemove(id).exec(); } catch (error) { throw new BadRequestException(error.toString()); } diff --git a/src/modules/goods/goods.controller.ts b/src/modules/goods/goods.controller.ts index d4a6cde..b1b081e 100644 --- a/src/modules/goods/goods.controller.ts +++ b/src/modules/goods/goods.controller.ts @@ -1,6 +1,7 @@ import { Controller, Req, Res, Body, Get, Post, Param, Session, - HttpStatus, BadRequestException, UseGuards, Delete, HttpCode, Query, Put + HttpStatus, BadRequestException, UseGuards, Delete, HttpCode, Query, Put, + UploadedFile, UploadedFiles, UsePipes, UseInterceptors } from "@nestjs/common"; import { ApiBearerAuth, ApiUseTags, ApiResponse, ApiOperation, ApiImplicitParam, @@ -12,7 +13,7 @@ import { ObjectId } from "@models/common"; import { config } from "@utils/config"; import { RolesGuard } from "@guards/roles"; import { Roles } from "@decorators/roles"; -import { File, Files, User } from "@decorators/route"; +import { User } from "@decorators/route"; import { GidDto } from "@dtos/ids"; import { IReqUser } from "@dtos/req"; import { PerPageDto, ListResponse } from "@dtos/page"; @@ -33,6 +34,7 @@ import { isArray } from "util"; import { CreateValueDto, EditValueDto } from "../values/values.dto"; import { GoodAttributeParamDto, UploadQueryDto, EditBodyDto } from "./goods.dto"; +import { RegexpCountCheckInterceptor } from "@interceptors/regexp-count-check"; @UseGuards(RolesGuard) @Controller("api/v1/goods") @@ -144,6 +146,7 @@ export class GoodsAdminController { @Roles("admin", "token") @Post() + @UseInterceptors(RegexpCountCheckInterceptor) // region Swagger Docs @HttpCode(HttpStatus.CREATED) @ApiOperation({ title: "上传单个文件" }) @@ -153,7 +156,7 @@ export class GoodsAdminController { }) // endregion Swagger Docs public async addGood( - @File(new RegexpCountCheckPipe()) file: Express.Multer.File, + @UploadedFile() file: Express.Multer.File, @User() user: IReqUser, @Session() session, @Query(new ToArrayPipe()) query: UploadQueryDto ) { @@ -184,6 +187,7 @@ export class GoodsAdminController { @Roles("admin", "token") @Post("/collections") + @UseInterceptors(RegexpCountCheckInterceptor) // region Swagger Docs @HttpCode(HttpStatus.CREATED) @ApiOperation({ title: "上传多个文件并形成文件集" }) @@ -193,7 +197,7 @@ export class GoodsAdminController { }) // endregion Swagger Docs public async addGoods( - @Files(new RegexpCountCheckPipe()) files: Express.Multer.File[], + @UploadedFiles() files: Express.Multer.File[], @User() user: IReqUser, @Session() session, @Query(new ToArrayPipe()) query: UploadQueryDto ) { diff --git a/test/api/categroies.e2e.ts b/test/api/categories/categories.e2e.ts similarity index 91% rename from test/api/categroies.e2e.ts rename to test/api/categories/categories.e2e.ts index 931a465..467a5fd 100644 --- a/test/api/categroies.e2e.ts +++ b/test/api/categories/categories.e2e.ts @@ -1,9 +1,9 @@ import supertest = require("supertest"); -import { connect, drop, newUser } from "../helpers/database"; -import { init } from "../helpers/server"; -import { login } from "../helpers/database/auth"; -import { newName } from "../helpers/utils"; +import { connect, drop, newUser } from "../../helpers/database"; +import { init } from "../../helpers/server"; +import { login } from "../../helpers/database/auth"; +import { newName } from "../../helpers/utils"; describe("Categories E2E Api", () => { diff --git a/test/api/collections.e2e.ts b/test/api/collections/collections.e2e.ts similarity index 97% rename from test/api/collections.e2e.ts rename to test/api/collections/collections.e2e.ts index 13db734..4b4602c 100644 --- a/test/api/collections.e2e.ts +++ b/test/api/collections/collections.e2e.ts @@ -7,13 +7,13 @@ import { Model as GoodsModels } from "@models/Good"; import { connect, drop, newUser, addCategoryAndRegexp -} from "../helpers/database"; -import { init } from "../helpers/server"; -import { uploadFiles } from "../helpers/files"; -import { sleep, newName } from "../helpers/utils"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; +import { uploadFiles } from "../../helpers/files"; +import { sleep, newName } from "../../helpers/utils"; import auth = require("@db/auth"); import goodsDb = require("@db/goods"); -import files = require("../helpers/files"); +import files = require("../../helpers/files"); describe("Collections E2E Api", () => { diff --git a/test/api/collections_token.e2e.ts b/test/api/collections/collections_token.e2e.ts similarity index 95% rename from test/api/collections_token.e2e.ts rename to test/api/collections/collections_token.e2e.ts index c1ebdec..b0faa93 100644 --- a/test/api/collections_token.e2e.ts +++ b/test/api/collections/collections_token.e2e.ts @@ -8,9 +8,9 @@ import { Model as GoodsModels } from "@models/Good"; import { connect, drop, newUser, addCategoryAndRegexp -} from "../helpers/database"; -import { init } from "../helpers/server"; -import { uploadFiles, newFile } from "../helpers/files"; +} from "../../helpers/database"; +import { init } from "../../helpers/server"; +import { uploadFiles, newFile } from "../../helpers/files"; describe("Token to Upload Files Api", () => { diff --git a/test/api/goods.e2e.ts b/test/api/goods/goods.e2e.ts similarity index 84% rename from test/api/goods.e2e.ts rename to test/api/goods/goods.e2e.ts index aeb4d8b..67673d1 100644 --- a/test/api/goods.e2e.ts +++ b/test/api/goods/goods.e2e.ts @@ -1,11 +1,11 @@ import supertest = require("supertest"); import path = require("path"); -import { connect, drop, addCategoryAndRegexp } from "../helpers/database"; -import { uploadFile } from "../helpers/files"; -import { init } from "../helpers/server"; -import auth = require("../helpers/database/auth"); -import { newName } from "../helpers/utils"; +import { connect, drop, addCategoryAndRegexp } from "../../helpers/database"; +import { uploadFile } from "../../helpers/files"; +import { init } from "../../helpers/server"; +import auth = require("@db/auth"); +import { newName } from "../../helpers/utils"; describe("Goods E2E Api", () => { @@ -47,7 +47,7 @@ describe("Goods E2E Api", () => { let result; step("Upload File", async () => { - const filepath = `${__dirname}/../files/icon_pandorabox_64x64.png`; + const filepath = `${__dirname}/../../files/icon_pandorabox_64x64.png`; // Create result = await uploadFile(request, filepath); result = result.body; diff --git a/test/api/goods_append.e2e.ts b/test/api/goods/goods_append.e2e.ts similarity index 93% rename from test/api/goods_append.e2e.ts rename to test/api/goods/goods_append.e2e.ts index a363100..03e9086 100644 --- a/test/api/goods_append.e2e.ts +++ b/test/api/goods/goods_append.e2e.ts @@ -1,13 +1,13 @@ import * as supertest from "supertest"; import path = require("path"); -import { init } from "../helpers/server"; -import db = require("../helpers/database"); +import { init } from "../../helpers/server"; +import db = require("../../helpers/database"); import auth = require("@db/auth"); import goods = require("@db/goods"); import regexps = require("@db/regexps"); -import files = require("../helpers/files"); +import files = require("../../helpers/files"); import categories = require("@db/categories"); -import { newName } from "../helpers/utils"; +import { newName } from "../../helpers/utils"; describe("Upload Good with Append categories", () => { diff --git a/test/api/goods_specified_category.e2e.ts b/test/api/goods/goods_specified_category.e2e.ts similarity index 95% rename from test/api/goods_specified_category.e2e.ts rename to test/api/goods/goods_specified_category.e2e.ts index b56d6a4..ff5b253 100644 --- a/test/api/goods_specified_category.e2e.ts +++ b/test/api/goods/goods_specified_category.e2e.ts @@ -1,13 +1,13 @@ import * as supertest from "supertest"; -import db = require("../helpers/database"); +import db = require("../../helpers/database"); import path = require("path"); -import files = require("../helpers/files"); +import files = require("../../helpers/files"); import auth = require("@db/auth"); import categories = require("@db/categories"); import * as regexps from "@db/regexps"; import * as goods from "@db/goods"; -import { init } from "../helpers/server"; -import { newName } from "../helpers/utils"; +import { init } from "../../helpers/server"; +import { newName } from "../../helpers/utils"; describe("Upload Good with specified categories", () => { diff --git a/test/helpers/files.ts b/test/helpers/files.ts index ba82866..acbee9f 100644 --- a/test/helpers/files.ts +++ b/test/helpers/files.ts @@ -85,6 +85,7 @@ export const remove = (filepaths: string[] | string) => { if (filepaths.length === 0) { return Promise.resolve(); } + filepaths = Array.from(new Set(filepaths)); return Promise.all(filepaths.map(async (filepath) => { await sleep(200); return fs.existsSync(filepath) ? fs.remove(filepath) : null; diff --git a/test/tsconfig.json b/test/tsconfig.json index a0ec75e..a52a71c 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -26,6 +26,7 @@ "@guards/*": [ "./src/modules/common/guards/*.guard" ], "@dtos/*": [ "./src/modules/common/dtos/*.dto" ], "@services/*": [ "./src/modules/common/services/*.service" ], + "@interceptors/*": [ "./src/modules/common/interceptors/*.interceptor" ], "@db/*": [ "./test/helpers/database/*" ] } }, diff --git a/tsconfig.json b/tsconfig.json index 1f8cf1b..a70a974 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,6 +34,10 @@ "./src/modules/common/dtos/*.dto", "./dist/modules/common/dtos/*.dto" ], + "@interceptors/*": [ + "./src/modules/common/interceptors/*.interceptor", + "./dist/modules/common/interceptors/*.interceptor" + ], "@services/*": [ "./src/modules/common/services/*.service", "./dist/modules/common/services/*.service" From f4b2fd64b823e222dccbfbd2dadb734c5d72a024 Mon Sep 17 00:00:00 2001 From: Arylo Date: Fri, 16 Mar 2018 18:43:52 +0800 Subject: [PATCH 37/49] Remove Non-exist module --- .travis.yml | 3 +-- src/modules/goods/goods.controller.ts | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index aee4e46..cc332d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,7 @@ install: - "npm install" script: - - "npm run tsc" - - "npm test" + - "npm run tsc && npm test" after_success: - npm install coveralls@~3.0.0 --global diff --git a/src/modules/goods/goods.controller.ts b/src/modules/goods/goods.controller.ts index b1b081e..ee92ec0 100644 --- a/src/modules/goods/goods.controller.ts +++ b/src/modules/goods/goods.controller.ts @@ -18,7 +18,6 @@ import { GidDto } from "@dtos/ids"; import { IReqUser } from "@dtos/req"; import { PerPageDto, ListResponse } from "@dtos/page"; import { DefResDto } from "@dtos/res"; -import { RegexpCountCheckPipe } from "@pipes/regexp-count-check"; import { ParseIntPipe } from "@pipes/parse-int"; import { ToArrayPipe } from "@pipes/to-array"; import { TokensService } from "@services/tokens"; From 7e7d5d1ca43441da78c9f148faa41bddf3c1ae2c Mon Sep 17 00:00:00 2001 From: Arylo Date: Sat, 17 Mar 2018 17:40:40 +0800 Subject: [PATCH 38/49] update --- src/express.ts | 1 + src/modules/common/services/goods.service.ts | 8 +++----- src/modules/common/services/system.service.ts | 14 +++++++++++--- src/modules/files/files.controller.ts | 4 ++-- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/express.ts b/src/express.ts index 8e59413..27e29cb 100644 --- a/src/express.ts +++ b/src/express.ts @@ -34,6 +34,7 @@ export const initExpress = () => { saveUninitialized: true, cookie: { secure: false, maxAge: 7200 * 1000 } }; + /* istanbul ignore if */ if (!isTest) { sessionOpts.store = new RedisStore({ url: config.redis.url diff --git a/src/modules/common/services/goods.service.ts b/src/modules/common/services/goods.service.ts index c99c90b..7e5868c 100644 --- a/src/modules/common/services/goods.service.ts +++ b/src/modules/common/services/goods.service.ts @@ -25,7 +25,7 @@ export class GoodsService extends BaseService { } public get(cond: object, opts?: IGetOptions) { - let p = GoodsModels.findOne(cond); + let p = GoodsModels.find(cond); p = this.documentQueryProcess(p, opts); return p.exec(); } @@ -127,10 +127,8 @@ export class GoodsService extends BaseService { * Get Good by Good ID * @param id Good ID */ - public getById(id: ObjectId, opts?: IGetOptions) { - let p = GoodsModels.findById(id); - p = this.documentQueryProcess(p, opts); - return p.exec(); + public async getById(id: ObjectId, opts?: IGetOptions) { + return (await this.get({ _id: id }, opts))[0]; } /** diff --git a/src/modules/common/services/system.service.ts b/src/modules/common/services/system.service.ts index c131a45..d4d6fc2 100644 --- a/src/modules/common/services/system.service.ts +++ b/src/modules/common/services/system.service.ts @@ -1,8 +1,10 @@ -import { Component } from "@nestjs/common"; +import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; import { Model as SystemModel } from "@models/System"; import { Model as UsergroupsModel } from "@models/Usergroup"; +import { systemLogger } from "../helper/log"; + @Component() export class SystemService { @@ -17,6 +19,7 @@ export class SystemService { key: SystemService.DEFAULT_USERGROUP_FLAG }).exec(); if (!gid) { + systemLogger.warn(`Miss ${SystemService.DEFAULT_USERGROUP_FLAG}`); gid = (await UsergroupsModel.findOne().exec())._id; this.setDefaultUsergroup(gid); } @@ -27,8 +30,13 @@ export class SystemService { * Set Default Usergroup ID * @param gid Usergroup ID */ - public setDefaultUsergroup(gid: ObjectId) { - return SystemModel.findOneAndUpdate( + public async setDefaultUsergroup(gid: ObjectId) { + const doc = await UsergroupsModel.findById(gid).exec(); + if (!doc) { + throw new BadRequestException("The ID isnt a Usergroup ID"); + } + return SystemModel + .findOneAndUpdate( { key: SystemService.DEFAULT_USERGROUP_FLAG }, { value: gid }, { upsert: true } ) diff --git a/src/modules/files/files.controller.ts b/src/modules/files/files.controller.ts index e9aeb32..dac526b 100644 --- a/src/modules/files/files.controller.ts +++ b/src/modules/files/files.controller.ts @@ -40,9 +40,9 @@ export class FilesController { ) { let obj: GoodDoc; try { - obj = await this.goodsSvr.get({ + obj = (await this.goodsSvr.get({ _id: params.id, category: params.cid - }); + }))[0]; } catch (error) { throw new BadRequestException(error.toString()); } From 30b743e13acd418ba9a7d6948411decd8e3824ae Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 19 Mar 2018 17:53:46 +0800 Subject: [PATCH 39/49] set Cache to Redis --- TODOLIST.md | 4 +- package.json | 5 ++- src/models/Categroy.ts | 10 ++++- src/models/Collection.ts | 12 ++++-- src/models/Good.ts | 10 ++++- src/models/Regexp.ts | 9 ++++- src/models/Token.ts | 9 ++++- src/models/User.ts | 9 ++++- src/modules/common/services/base.service.ts | 28 +++++++------ .../common/services/categories.service.ts | 4 +- .../common/services/regexps.service.ts | 40 +++++++++++++++---- src/modules/common/services/users.service.ts | 15 +++++-- src/modules/goods/goods.controller.ts | 31 +++++++------- 13 files changed, 127 insertions(+), 59 deletions(-) diff --git a/TODOLIST.md b/TODOLIST.md index 122df67..6260308 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -11,6 +11,6 @@ - [ ] 接入统计 - [x] 配置文件写入初始化用户账号密码 - [ ] 接入AuthBox -- [ ] Redis 接入 +- [x] Redis 接入 - [x] 支持Session - - [ ] 缓存整理 \ No newline at end of file + - [x] 缓存整理 \ No newline at end of file diff --git a/package.json b/package.json index 4bc41fc..3020418 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@types/fs-extra": "^4.0.4", "@types/hasha": "^3.0.0", "@types/helmet": "0.0.37", + "@types/is-promise": "^2.1.0", "@types/lodash": "^4.14.85", "@types/md5": "^2.1.32", "@types/mocha": "^2.2.44", @@ -67,6 +68,7 @@ "typescript": "^2.6.1" }, "dependencies": { + "@keyv/redis": "^1.3.8", "@nestjs/common": "4.6.5", "@nestjs/core": "4.6.5", "@nestjs/swagger": "1.1.4", @@ -82,6 +84,8 @@ "fs-extra": "^4.0.2", "hasha": "^3.0.0", "helmet": "^3.11.0", + "is-promise": "^2.1.0", + "keyv": "^3.0.0", "lodash": "^4.17.4", "md5": "^2.2.1", "md5-file": "^3.2.3", @@ -90,7 +94,6 @@ "path-exists": "^3.0.0", "reflect-metadata": "^0.1.10", "rxjs": "^5.5.2", - "schedule-cache": "^1.0.0", "useragent": "^2.2.1", "uuid": "^3.1.0", "y-config": "^1.1.5" diff --git a/src/models/Categroy.ts b/src/models/Categroy.ts index 6e1797b..8bc8fda 100644 --- a/src/models/Categroy.ts +++ b/src/models/Categroy.ts @@ -5,9 +5,15 @@ import { DEF_PER_COUNT } from "@dtos/page"; import { isArray } from "util"; import { reduce, includes, difference } from "lodash"; import { MongoError } from "mongodb"; -import Cache = require("schedule-cache"); +import { config } from "@utils/config"; +import keyv = require("keyv"); -export const cache = Cache.create(`${Date.now()}${Math.random()}`); +import { isTest } from "../modules/common/helper/env"; + +export const cache = new keyv({ + uri: isTest ? undefined : config.redis.url, + namespace: "Categories" +}); export const FLAG = "categories"; export type CategoryDoc = IDoc; diff --git a/src/models/Collection.ts b/src/models/Collection.ts index 370c775..c68ceb6 100644 --- a/src/models/Collection.ts +++ b/src/models/Collection.ts @@ -4,11 +4,17 @@ import { } from "@models/common"; import { IGoods, FLAG as GoodFlag, Model as GoodsModels } from "@models/Good"; import { IUser, FLAG as UserFlag } from "@models/User"; -import Cache = require("schedule-cache"); +import { config } from "@utils/config"; +import keyv = require("keyv"); -export const FLAG = "collections"; +import { isTest } from "../modules/common/helper/env"; + +export const cache = new keyv({ + uri: isTest ? undefined : config.redis.url, + namespace: "Collections" +}); -export const cache = Cache.create(`${Date.now()}${Math.random()}`); +export const FLAG = "collections"; const Definition: SchemaDefinition = { name: { diff --git a/src/models/Good.ts b/src/models/Good.ts index bd96b28..b0604ac 100644 --- a/src/models/Good.ts +++ b/src/models/Good.ts @@ -5,9 +5,15 @@ import { import { IValues, Flag as ValueFlag } from "@models/Value"; import { IUser, FLAG as UserFlag } from "@models/User"; import { ICategory, FLAG as CategoryFlag } from "@models/Categroy"; -import Cache = require("schedule-cache"); +import { config } from "@utils/config"; +import keyv = require("keyv"); -const cache = Cache.create(`${Date.now()}${Math.random()}`); +import { isTest } from "../modules/common/helper/env"; + +export const cache = new keyv({ + uri: isTest ? undefined : config.redis.url, + namespace: "Goods" +}); export const FLAG = "goods"; export type GoodDoc = IDoc; diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index 7ef62d1..2e1494b 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -4,12 +4,17 @@ import { } from "@models/common"; import { ICategory, FLAG as CF, Model as CM } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; -import Cache = require("schedule-cache"); import isRegExp = require("@utils/isRegExp"); +import { config } from "@utils/config"; +import keyv = require("keyv"); import { INewRegexp } from "../modules/regexps/regexps.dto"; +import { isTest } from "../modules/common/helper/env"; -export const cache = Cache.create(`${Date.now()}${Math.random()}`); +export const cache = new keyv({ + uri: isTest ? undefined : config.redis.url, + namespace: "Regexps" +}); const Definition: SchemaDefinition = { name: { type: String, required: true, unique: true }, diff --git a/src/models/Token.ts b/src/models/Token.ts index 17bd2bb..3eff4a7 100644 --- a/src/models/Token.ts +++ b/src/models/Token.ts @@ -3,10 +3,15 @@ import { Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS, existsValidator } from "@models/common"; import { IUser, FLAG as UserFlag } from "@models/User"; +import { config } from "@utils/config"; +import keyv = require("keyv"); -import Cache = require("schedule-cache"); +import { isTest } from "../modules/common/helper/env"; -export const cache = Cache.create(`${Date.now()}${Math.random()}`); +export const cache = new keyv({ + uri: isTest ? undefined : config.redis.url, + namespace: "Tokens" +}); export const Flag = "tokens"; diff --git a/src/models/User.ts b/src/models/User.ts index 4206ecd..6b10a86 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -3,10 +3,15 @@ import * as md5 from "md5"; import { config } from "@utils/config"; import { ObjectId, existsValidator } from "@models/common"; import { DEF_PER_COUNT } from "@dtos/page"; -import Cache = require("schedule-cache"); import { Base, IDoc, IDocRaw, MODIFY_MOTHODS } from "./common"; +import keyv = require("keyv"); -const cache = Cache.create(`${Date.now()}${Math.random()}`); +import { isTest } from "../modules/common/helper/env"; + +export const cache = new keyv({ + uri: isTest ? undefined : config.redis.url, + namespace: "Users" +}); export const FLAG = "users"; diff --git a/src/modules/common/services/base.service.ts b/src/modules/common/services/base.service.ts index 01246de..fdb8474 100644 --- a/src/modules/common/services/base.service.ts +++ b/src/modules/common/services/base.service.ts @@ -1,8 +1,8 @@ import { DEF_PER_COUNT } from "@dtos/page"; import { UtilService } from "@services/util"; import { DocumentQuery, ModelPopulateOptions } from "mongoose"; - -type TimeType = number | string; +import keyv = require("keyv"); +import isPromise = require("is-promise"); export interface IGetOptions { populate?: Array; @@ -14,28 +14,30 @@ export abstract class BaseService { protected readonly DEF_UPDATE_OPTIONS = { runValidators: true, context: "query" }; - private cache; + private cache: keyv; - protected setCache(cache) { + protected setCache(cache: keyv) { this.cache = cache; } - protected loadAndCache( - FLAG: string, value: () => T, time?: TimeType - ): T { + protected async loadAndCache( + FLAG: string, value: () => Promise, time?: number + ): Promise; + protected async loadAndCache( + FLAG: string, value: () => T, time = 1000 * 60 * 5 // 5 min + ): Promise { if (!this.cache) { return value(); } - const c = this.cache.get(FLAG); + const c: T = await this.cache.get(FLAG); if (c) { return c; } - const val = value(); - if (time) { - this.cache.put(FLAG, val, time); - } else { - this.cache.put(FLAG, val); + let val = value(); + if (isPromise(val)) { + val = await val; } + await this.cache.set(FLAG, val, time); return val; } diff --git a/src/modules/common/services/categories.service.ts b/src/modules/common/services/categories.service.ts index 4682356..63f7087 100644 --- a/src/modules/common/services/categories.service.ts +++ b/src/modules/common/services/categories.service.ts @@ -109,7 +109,9 @@ export class CategoriesService extends BaseService { } public count() { - return CategoriesModel.count({ }).exec(); + return this.loadAndCache( + "count", () => CategoriesModel.count({ }).exec() + ); } public async add(ctx: object) { diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index 1bd47f5..af73eaa 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -92,20 +92,32 @@ export class RegexpsService extends BaseService { } public count() { - const FLAG = "totalCount"; + const FLAG = "total"; return this.loadAndCache( FLAG, () => RegexpsModel.count({ }).exec() ); } - public getById(id: ObjectId, opts?: IGetOptions) { - let p = RegexpsModel.findById(id) - .populate({ path: "link", populate: { path: "pid" } }); + public get(conditions: object, opts?: IGetOptions) { + let p = RegexpsModel.find(conditions); p = this.documentQueryProcess(p, opts); return p.exec(); } + public async getById(id: ObjectId, opts?: IGetOptions) { + const extraPopulate = { path: "link", populate: { path: "pid" } }; + if (!opts) { + opts = { }; + } + if (opts.populate) { + opts.populate.push(extraPopulate); + } else { + opts.populate = [ extraPopulate ]; + } + return (await this.get({ _id: id }, opts))[0]; + } + /** * 规则列表 * @param perNum {number} 每页数量 @@ -129,6 +141,9 @@ export class RegexpsService extends BaseService { const DEF_CONDITIONS = { link: { $exists: true }, hidden: false }; + const DEF_OPTIONS = { + populate: [ "link" ] + }; if (opts.categroies && opts.categroies.length > 0) { // 指定Categroy @@ -141,7 +156,10 @@ export class RegexpsService extends BaseService { }; return this.loadAndCache( FLAG, - () => RegexpsModel.find(conditions).populate("link").exec(), + async () => { + const result = await this.get(conditions, DEF_OPTIONS); + return result.map((item) => item.toObject()); + }, 50 ); } else if (opts.appends && opts.appends.length > 0) { @@ -155,14 +173,20 @@ export class RegexpsService extends BaseService { }; return this.loadAndCache( FLAG, - () => RegexpsModel.find(conditions).populate("link").exec(), + async () => { + const result = await this.get(conditions, DEF_OPTIONS); + return result.map((item) => item.toObject()); + }, 50 ); } else { const FLAG = "default_scan_regexps"; return this.loadAndCache( FLAG, - () => RegexpsModel.find(DEF_CONDITIONS).populate("link").exec(), + async () => { + const result = await this.get(DEF_CONDITIONS, DEF_OPTIONS); + return result.map((item) => item.toObject()); + }, 50 ); } @@ -176,7 +200,7 @@ export class RegexpsService extends BaseService { const result = await this.getRegexps(opts); const list = [ ]; result.forEach((item) => { - const obj = item.toObject(); + const obj = item; const reg = new RegExp(obj.value); if (reg.test(name)) { list.push(obj.link); diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index 96aeb0a..ba03ca0 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -98,7 +98,10 @@ export class UsersService extends BaseService { * 返回总数 */ public conut() { - return UsersModel.count({ }).exec(); + return this.loadAndCache( + "count", + () => UsersModel.count({ }).exec() + ); } /** @@ -108,9 +111,13 @@ export class UsersService extends BaseService { * @param opts.page {number} 页数 */ public list(opts = this.DEF_PER_OBJ) { - return UsersModel.find().select("-password") - .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) - .exec(); + const Flag = `list_${opts.perNum}_${opts.page}`; + return this.loadAndCache( + Flag, + () => UsersModel.find().select("-password") + .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) + .exec() + ); } /** diff --git a/src/modules/goods/goods.controller.ts b/src/modules/goods/goods.controller.ts index ee92ec0..b400240 100644 --- a/src/modules/goods/goods.controller.ts +++ b/src/modules/goods/goods.controller.ts @@ -134,8 +134,9 @@ export class GoodsAdminController { } catch (error) { if (cb) { cb("Good", error); + } else { + throw error; } - return; } const newFilePath = `${config.paths.upload}/${categories[0]._id}/${obj.file.filename}`; @@ -222,22 +223,18 @@ export class GoodsAdminController { if (goods.length === 0) { throw new BadRequestException("The Collection no good"); } else { - try { - const collections = await this.collectionsSvr.create({ - goods: goods.reduce((arr, good) => { - arr.push(good._id); - return arr; - }, []), - creator: uploaderId - }); - const collection = - isArray(collections) ? collections[0] : collections; - return this.collectionsSvr.getById(collection._id, { - populate: [ "goods" ] - }); - } catch (error) { - throw new BadRequestException(error.toString()); - } + const collections = await this.collectionsSvr.create({ + goods: goods.reduce((arr, good) => { + arr.push(good._id); + return arr; + }, []), + creator: uploaderId + }); + const collection = + isArray(collections) ? collections[0] : collections; + return this.collectionsSvr.getById(collection._id, { + populate: [ "goods" ] + }); } } From 91ea0ef3883b398fa5e89d6a1b511eb8d2922b5c Mon Sep 17 00:00:00 2001 From: Arylo Date: Tue, 20 Mar 2018 18:34:14 +0800 Subject: [PATCH 40/49] Update Code Child Service Use Base Service(create,delete,modify,find) methods --- .../collections.admin.controller.ts | 5 +- src/modules/common/services/base.service.ts | 156 ++++++++++++++++-- .../common/services/categories.service.ts | 54 ++---- .../common/services/collections.service.ts | 69 +++----- src/modules/common/services/goods.service.ts | 86 ++++------ .../common/services/regexps.service.ts | 51 ++---- src/modules/common/services/system.service.ts | 2 + src/modules/common/services/tokens.service.ts | 41 ++--- .../common/services/usergroups.service.ts | 33 ++-- src/modules/common/services/users.service.ts | 40 ++--- 10 files changed, 289 insertions(+), 248 deletions(-) diff --git a/src/modules/collections/collections.admin.controller.ts b/src/modules/collections/collections.admin.controller.ts index 04fcacf..ba0bfbd 100644 --- a/src/modules/collections/collections.admin.controller.ts +++ b/src/modules/collections/collections.admin.controller.ts @@ -102,11 +102,10 @@ export class CollectionsAdminController { @ApiOperation({ title: "Get One Collection's Info" }) // endregion Swagger Docs public async getCollection(@Param() param: CCidDto) { - const doc = await this.collectionsSvr.getById(param.cid); - if (!doc) { + const obj = await this.collectionsSvr.getById(param.cid); + if (!obj) { return null; } - const obj = doc.toObject(); obj.goods = UtilService.toListRespone(obj.goods as any[], { perNum: obj.goods.length }) as any; diff --git a/src/modules/common/services/base.service.ts b/src/modules/common/services/base.service.ts index fdb8474..84f2e28 100644 --- a/src/modules/common/services/base.service.ts +++ b/src/modules/common/services/base.service.ts @@ -1,19 +1,148 @@ +import { BadGatewayException, BadRequestException } from "@nestjs/common"; +import { isString } from "util"; import { DEF_PER_COUNT } from "@dtos/page"; import { UtilService } from "@services/util"; -import { DocumentQuery, ModelPopulateOptions } from "mongoose"; +import { + DocumentQuery, ModelPopulateOptions, Model, ModelUpdateOptions +} from "mongoose"; import keyv = require("keyv"); import isPromise = require("is-promise"); +import { IDocRaw, IDoc, ObjectId } from "@models/common"; export interface IGetOptions { - populate?: Array; - select?: string[]; + populate?: string | Array; + select?: string | string[]; + perNum?: number; + page?: number; + sort?: string | object; } -export abstract class BaseService { +abstract class ModelService { - protected readonly DEF_UPDATE_OPTIONS = { + protected readonly DEF_UPDATE_OPTIONS: ModelUpdateOptions = { runValidators: true, context: "query" }; + private model: Model>; + + protected setModel>>(model: M) { + this.model = model; + } + + private checkModel() { + if (!this.model) { + throw new BadGatewayException("Lost Model"); + } + } + + public async create(obj: object) { + this.checkModel(); + try { + return await this.model.create(obj); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public async delete(cond: object) { + this.checkModel(); + try { + return await this.model.findOneAndRemove(cond).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public async deleteById(id: ObjectId) { + this.checkModel(); + try { + return await this.model.findByIdAndRemove(id).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + public async modifyById( + id: ObjectId, ctx: object, opts = this.DEF_UPDATE_OPTIONS + ) { + this.checkModel(); + const options = Object.assign({ }, this.DEF_UPDATE_OPTIONS, opts); + try { + return await this.model.update({ _id: id }, ctx, options).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + protected find(cond: object, opts?: IGetOptions) { + this.checkModel(); + const p = this.model.find(cond); + return this.documentQueryProcess(p, opts).exec(); + } + + protected findObjects(cond: object, opts?: IGetOptions) { + return this.find(cond, opts).then((arr) => { + return arr.map((item) => item.toObject()); + }); + } + + protected findOne(cond: object, opts?: IGetOptions) { + this.checkModel(); + const p = this.model.findOne(cond); + return this.documentQueryProcess(p, opts).exec(); + } + + protected findObject(cond: object, opts?: IGetOptions) { + return this.findOne(cond, opts).then((item) => { + return !item ? null : item.toObject(); + }); + } + + protected findById(id: ObjectId, opts?: IGetOptions) { + this.checkModel(); + const p = this.model.findById(id); + return this.documentQueryProcess(p, opts).exec(); + } + + protected findObjectById(id: ObjectId, opts?: IGetOptions) { + return this.findById(id, opts).then((item) => { + return !item ? null : item.toObject(); + }); + } + + protected total(cond: object = { }) { + this.checkModel(); + return this.model.count(cond).exec(); + } + + private documentQueryProcess>( + query: T, opts: IGetOptions = { } + ) { + if (opts.sort) { + query = query.sort(opts.sort); + } + if (isString(opts.populate)) { + opts.populate = [ opts.populate ]; + } + for (const p of (opts.populate || [ ])) { + query = query.populate(p); + } + if (isString(opts.select)) { + opts.select = [ opts.select ]; + } + for (const s of (opts.select || [ ])) { + query = query.select(s); + } + if (opts.perNum && opts.page) { + query = query + .skip((opts.page - 1) * opts.perNum) + .limit(opts.perNum); + } + return query; + } +} + +export abstract class BaseService extends ModelService { + private cache: keyv; protected setCache(cache: keyv) { @@ -50,16 +179,13 @@ export abstract class BaseService { */ public calPageCount = UtilService.calPageCount; - public documentQueryProcess>( - query: T, opts: IGetOptions = { } - ) { - for (const p of (opts.populate || [ ])) { - query = query.populate(p); - } - for (const s of (opts.select || [ ])) { - query = query.select(s); - } - return query; + protected total(cond: object = { }) { + const flag = Object.keys(cond).sort().reduce((f, key) => { + return `${f}_${key}_${cond[key].toString()}`; + }, "total"); + return this.loadAndCache( + flag, () => super.total(cond) + ); } } diff --git a/src/modules/common/services/categories.service.ts b/src/modules/common/services/categories.service.ts index 63f7087..edde4fb 100644 --- a/src/modules/common/services/categories.service.ts +++ b/src/modules/common/services/categories.service.ts @@ -11,20 +11,21 @@ interface IIdMap { } @Component() -export class CategoriesService extends BaseService { +export class CategoriesService extends BaseService { constructor(private readonly goodsSvr: GoodsService) { super(); super.setCache(cache); + super.setModel(CategoriesModel); } private getIdMap() { return this.loadAndCache("IdMap", async () => { // { parentId: childrenIds } const map: IIdMap = { }; - const docs = await CategoriesModel.find().select("_id pid").exec(); - docs.forEach((doc) => { - const category = doc.toObject(); + const categories = + await this.findObjects({ }, { select: "_id pid" }); + categories.forEach((category) => { let index; if (!category.pid) { index = "*"; @@ -77,13 +78,12 @@ export class CategoriesService extends BaseService { return arr; }, []) }; - const p = (await this.get(conditions, { + const p = (await this.findObjects(conditions, { populate: [ "attributes", { path: "pid", populate: { path: "pid" } } ] })) - .map((item) => item.toObject()) .map((item) => { item.tags = Array.from(new Set(this.getTags(item))); delete item.pid; @@ -103,44 +103,27 @@ export class CategoriesService extends BaseService { * @return {Promise} */ public async list(opts = this.DEF_PER_OBJ) { - return CategoriesModel.find({ }) - .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) - .exec(); + return this.find({ }, opts); } public count() { - return this.loadAndCache( - "count", () => CategoriesModel.count({ }).exec() - ); + return this.total({ }); } - public async add(ctx: object) { - try { - return await CategoriesModel.create(ctx); - } catch (error) { - throw new BadRequestException(error.toString()); - } + public add(ctx: object) { + return this.create(ctx); } public get(cond: object, opts?: IGetOptions) { - let p = CategoriesModel.find(cond); - p = this.documentQueryProcess(p, opts); - return p.exec(); + return this.find(cond, opts); } public async getById(id: ObjectId, opts?: IGetOptions) { - const arr = await this.get({ _id: id }, opts); - return arr.length === 0 ? null : arr[0]; + return this.findById(id, opts); } - public async editById(id: ObjectId, ctx: object) { - try { - return await CategoriesModel - .update({ _id: id }, ctx, this.DEF_UPDATE_OPTIONS) - .exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + public editById(id: ObjectId, ctx: object) { + return this.modifyById(id, ctx); } public async removeById(id: ObjectId) { @@ -148,17 +131,12 @@ export class CategoriesService extends BaseService { if (goods.length > 0) { throw new BadRequestException("Some Good in the category"); } - const map = await this.getIdMap(); - if (map[id.toString()] && map[id.toString()].length > 0) { + if ((await this.getChildrenIds(id)).length > 0) { throw new BadRequestException( "The Category have some child categories" ); } - try { - return await CategoriesModel.findByIdAndRemove(id).exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + return this.deleteById(id); } } diff --git a/src/modules/common/services/collections.service.ts b/src/modules/common/services/collections.service.ts index 2591e47..b331795 100644 --- a/src/modules/common/services/collections.service.ts +++ b/src/modules/common/services/collections.service.ts @@ -1,59 +1,46 @@ import { Component, Param, BadRequestException } from "@nestjs/common"; import { UidDto } from "@dtos/ids"; -import { Model as CollectionsModel, cache } from "@models/Collection"; +import { + Model as CollectionsModel, cache, ICollections +} from "@models/Collection"; import { ObjectId } from "@models/common"; import { DEF_PER_COUNT } from "@dtos/page"; import { IEditCollection } from "../../../modules/collections/collections.dto"; import { BaseService, IGetOptions } from "@services/base"; @Component() -export class CollectionsService extends BaseService { +export class CollectionsService extends BaseService { constructor() { super(); this.setCache(cache); + this.setModel(CollectionsModel); } - public async create(obj) { - try { - return await CollectionsModel.create(obj); - } catch (error) { - throw new BadRequestException(error.toString()); - } + public create(obj: object) { + return super.create(obj); } - public async edit(cid: ObjectId, ctx: IEditCollection) { - try { - return await CollectionsModel - .update({ _id: cid }, ctx, this.DEF_UPDATE_OPTIONS) - .exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + public edit(cid: ObjectId, ctx: IEditCollection) { + return this.modifyById(cid, ctx); } public list(uid: ObjectId, pageObj = this.DEF_PER_OBJ) { - const perNum = pageObj.perNum; - const page = pageObj.page; + const perNum = pageObj.perNum || this.DEF_PER_OBJ.perNum; + const page = pageObj.page || this.DEF_PER_OBJ.page; return this.loadAndCache( `list_${uid.toString()}_${perNum}_${page}`, - () => { - return CollectionsModel.find({ creator: uid }) - .skip((page - 1) * perNum).limit(perNum) - .sort({ updatedAt: -1 }) - .populate("creator") - .populate("goods") - .exec(); - }, - 50 + () => this.findObjects({ creator: uid }, { + sort: { updatedAt: -1 }, + page, perNum, + populate: [ "creator", "goods" ] + }), + 1000 ); } public count(uid: ObjectId) { - return this.loadAndCache( - `count_${uid.toString()}`, - () => CollectionsModel.count({ creator: uid }).exec() - ); + return this.total({ creator: uid }); } private readonly GET_OPTIONS: IGetOptions = { @@ -65,12 +52,10 @@ export class CollectionsService extends BaseService { * @param name Collection Name */ public getByName(name: string, opts = this.GET_OPTIONS) { - let p = CollectionsModel.findOne({ name }); - p = this.documentQueryProcess(p, opts); return this.loadAndCache( `getByName_${name}`, - () => p.exec(), - 50 + () => this.findObject({ name }, opts), + 1000 ); } @@ -79,21 +64,15 @@ export class CollectionsService extends BaseService { * @param id Collection ID */ public getById(id: ObjectId, opts = this.GET_OPTIONS) { - let p = CollectionsModel.findById(id); - p = this.documentQueryProcess(p, opts); return this.loadAndCache( `getById_${id.toString()}`, - () => p.exec(), - 50 + () => this.findObjectById(id, opts), + 1000 ); } - public async remove(cid: ObjectId) { - try { - return await CollectionsModel.findByIdAndRemove(cid).exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + public remove(cid: ObjectId) { + return this.deleteById(cid); } } diff --git a/src/modules/common/services/goods.service.ts b/src/modules/common/services/goods.service.ts index 7e5868c..98f002e 100644 --- a/src/modules/common/services/goods.service.ts +++ b/src/modules/common/services/goods.service.ts @@ -9,7 +9,12 @@ import { config } from "@utils/config"; import fs = require("fs-extra"); @Component() -export class GoodsService extends BaseService { +export class GoodsService extends BaseService { + + constructor() { + super(); + this.setModel(GoodsModels); + } private getConditionsByCids(cids: ObjectId[]) { return cids.length === 1 ? { @@ -25,24 +30,21 @@ export class GoodsService extends BaseService { } public get(cond: object, opts?: IGetOptions) { - let p = GoodsModels.find(cond); - p = this.documentQueryProcess(p, opts); - return p.exec(); + return this.find(cond, opts); } public listByCategoryId(cid: ObjectId, pageObj = this.DEF_PER_OBJ) { return this.loadAndCache( `list_category_${cid.toString()}`, - () => GoodsModels.find({ category: cid }) - .populate("uploader") - .populate("attributes") - .select("-category") - .exec(), - 50 + () => this.findObjects({ category: cid }, Object.assign({ + populate: [ "uploader", "attributes" ], + select: "-category" + }, pageObj)), + 1000 ); } - public async countByCids(cids: ObjectId | ObjectId[]) { + public countByCids(cids: ObjectId | ObjectId[]) { if (!isArray(cids)) { cids = [ cids ]; } @@ -50,17 +52,15 @@ export class GoodsService extends BaseService { return [ ]; } const conditions = this.getConditionsByCids(cids); - return await GoodsModels.count(conditions).exec(); + return this.total(conditions); } - public async countByUids( - uids: ObjectId | ObjectId[], perNum = this.DEF_PER_OBJ.perNum - ) { + public countByUids(uids: ObjectId | ObjectId[]) { if (!isArray(uids)) { uids = [ uids ]; } const conditions = this.getConditionsByUids(uids); - return await GoodsModels.count(conditions).exec(); + return this.total(conditions); } /** @@ -74,34 +74,28 @@ export class GoodsService extends BaseService { cids = [ cids ]; } const conditions = this.getConditionsByCids(cids); - return GoodsModels.find(conditions) - .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) - .populate("uploader attributes") - .sort({ updatedAt: -1 }) - .exec(); + return this.find(conditions, Object.assign({ + populate: "uploader attributes", + sort: { updatedAt: -1 } + }, opts)); } private getConditionsByUids(uids: ObjectId[]) { - let conditions; switch (uids.length) { case 0: - conditions = { }; - break; + return { }; case 1: - conditions = { + return { uploader: uids[0] }; - break; default: - conditions = { + return { $or: uids.reduce((arr, uid) => { arr.push({ uploader: uid }); return arr; }, [ ]) }; - break; } - return conditions; } /** @@ -115,48 +109,40 @@ export class GoodsService extends BaseService { uids = [ uids ]; } const conditions = this.getConditionsByUids(uids); - return GoodsModels.find(conditions) - .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) - .select("-uploader") - .populate("attributes") - .sort({ updatedAt: -1 }) - .exec(); + return this.find(conditions, Object.assign({ + select: "-uploader", + populate: "attributes", + sort: { updatedAt: -1 } + }, opts)); } /** * Get Good by Good ID * @param id Good ID */ - public async getById(id: ObjectId, opts?: IGetOptions) { - return (await this.get({ _id: id }, opts))[0]; + public getById(id: ObjectId, opts?: IGetOptions) { + return super.findById(id, opts); } /** * Edit Good by Good ID * @param id Good ID */ - public async editById(id: ObjectId, ctx: object) { - try { - await GoodsModels - .update({ _id: id }, ctx, this.DEF_UPDATE_OPTIONS) - .exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + public editById(id: ObjectId, ctx: object) { + return this.modifyById(id, ctx); } public add(ctx: object) { - return GoodsModels.create(ctx); + return this.create(ctx); } public async remove(id: ObjectId) { - const doc = await this.getById(id); - if (!doc) { + const good = await this.findObjectById(id); + if (!good) { throw new BadRequestException("Non Exist Good ID"); } - const good = doc.toObject(); + this.deleteById(id); try { - await GoodsModels.findByIdAndRemove(id).exec(); if (good.attributes.length > 0) { const cond = (good.attributes as ObjectId[]) .reduce((obj, item) => { diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index af73eaa..cf4d9f8 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -1,11 +1,11 @@ import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; import { - Model as RegexpsModel, cache, RegexpDoc, IRegexpDoc + Model as RegexpsModel, cache, RegexpDoc, IRegexpDoc, IRegexp } from "@models/Regexp"; import { Model as CategroiesModel, ICategory } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; -import { isUndefined } from "util"; +import { isArray } from "util"; import { BaseService, IGetOptions } from "@services/base"; export interface IGetRegexpsOptions { @@ -14,11 +14,12 @@ export interface IGetRegexpsOptions { } @Component() -export class RegexpsService extends BaseService { +export class RegexpsService extends BaseService { constructor() { super(); - super.setCache(cache); + this.setCache(cache); + this.setModel(RegexpsModel); // Update setTimeout(() => { // Add Hidden Label @@ -33,11 +34,7 @@ export class RegexpsService extends BaseService { * 新增规则 */ public async create(obj: IRegexpDoc) { - try { - return await RegexpsModel.create(obj); - } catch (error) { - throw new BadRequestException(error.toString()); - } + return super.create(obj); } /** @@ -100,9 +97,7 @@ export class RegexpsService extends BaseService { } public get(conditions: object, opts?: IGetOptions) { - let p = RegexpsModel.find(conditions); - p = this.documentQueryProcess(p, opts); - return p.exec(); + return this.find(conditions, opts); } public async getById(id: ObjectId, opts?: IGetOptions) { @@ -110,7 +105,7 @@ export class RegexpsService extends BaseService { if (!opts) { opts = { }; } - if (opts.populate) { + if (opts.populate && isArray(opts.populate)) { opts.populate.push(extraPopulate); } else { opts.populate = [ extraPopulate ]; @@ -130,10 +125,11 @@ export class RegexpsService extends BaseService { const FLAG = `list_${perNum}_${page}`; return this.loadAndCache( FLAG, - () => RegexpsModel.find({ }) - .skip((page - 1) * perNum).limit(perNum) - .populate("link").exec(), - 50 + () => this.findObjects({ }, { + perNum, page, + populate: "link" + }), + 1000 ); } @@ -155,12 +151,7 @@ export class RegexpsService extends BaseService { }, [ ]) }; return this.loadAndCache( - FLAG, - async () => { - const result = await this.get(conditions, DEF_OPTIONS); - return result.map((item) => item.toObject()); - }, - 50 + FLAG, () => this.findObjects(conditions, DEF_OPTIONS), 1000 ); } else if (opts.appends && opts.appends.length > 0) { // 追加Categroy @@ -172,22 +163,12 @@ export class RegexpsService extends BaseService { }, [ DEF_CONDITIONS ]) }; return this.loadAndCache( - FLAG, - async () => { - const result = await this.get(conditions, DEF_OPTIONS); - return result.map((item) => item.toObject()); - }, - 50 + FLAG, () => this.findObjects(conditions, DEF_OPTIONS), 1000 ); } else { const FLAG = "default_scan_regexps"; return this.loadAndCache( - FLAG, - async () => { - const result = await this.get(DEF_CONDITIONS, DEF_OPTIONS); - return result.map((item) => item.toObject()); - }, - 50 + FLAG, () => this.findObjects(DEF_CONDITIONS, DEF_OPTIONS), 1000 ); } } diff --git a/src/modules/common/services/system.service.ts b/src/modules/common/services/system.service.ts index d4d6fc2..5553f8f 100644 --- a/src/modules/common/services/system.service.ts +++ b/src/modules/common/services/system.service.ts @@ -18,6 +18,7 @@ export class SystemService { let gid: any = await SystemModel.findOne({ key: SystemService.DEFAULT_USERGROUP_FLAG }).exec(); + /* istanbul ignore if */ if (!gid) { systemLogger.warn(`Miss ${SystemService.DEFAULT_USERGROUP_FLAG}`); gid = (await UsergroupsModel.findOne().exec())._id; @@ -32,6 +33,7 @@ export class SystemService { */ public async setDefaultUsergroup(gid: ObjectId) { const doc = await UsergroupsModel.findById(gid).exec(); + /* istanbul ignore if */ if (!doc) { throw new BadRequestException("The ID isnt a Usergroup ID"); } diff --git a/src/modules/common/services/tokens.service.ts b/src/modules/common/services/tokens.service.ts index cb12b89..63c5ff2 100644 --- a/src/modules/common/services/tokens.service.ts +++ b/src/modules/common/services/tokens.service.ts @@ -1,25 +1,21 @@ +import { BaseService } from "@services/base"; import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; -import { Model as TokensModel } from "@models/Token"; +import { Model as TokensModel, cache } from "@models/Token"; import { IUser } from "@models/User"; import { IUidDto } from "@dtos/ids"; @Component() -export class TokensService { +export class TokensService extends BaseService { - public async create(obj) { - try { - return await TokensModel.create(obj); - } catch (error) { - throw new BadRequestException(error.toString()); - } + constructor() { + super(); + this.setCache(cache); + this.setModel(TokensModel); } public getRawTokens(uid: ObjectId) { - return TokensModel - .find({ user: uid }) - .select("-user") - .exec(); + return this.find({ user: uid }, { select: "-uesr" }); } public async getTokens(uid: ObjectId) { @@ -32,20 +28,15 @@ export class TokensService { } public async getIdByToken(token: string) { - const t = await TokensModel.findOne({ token: token }); + const t = await this.findOne({ token }); if (t) { return t._id as ObjectId; - } else { - throw new BadRequestException("The token isnt exist"); } + throw new BadRequestException("The token isnt exist"); } - public async remove(obj) { - try { - return await TokensModel.findOneAndRemove(obj).exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + public remove(obj: object) { + return this.delete(obj); } /** @@ -54,9 +45,11 @@ export class TokensService { * @param token */ public async isVaild(username: string, token: string) { - const t = await TokensModel.findOne({ token }) - .populate("user").exec(); - const tokenOwn = t.toObject().user as IUser; + const obj = await this.findObject({ token }, { populate: "user" }); + if (!obj) { + return false; + } + const tokenOwn = obj.user as IUser; return tokenOwn.username === username && tokenOwn.active; } diff --git a/src/modules/common/services/usergroups.service.ts b/src/modules/common/services/usergroups.service.ts index eaff437..a783908 100644 --- a/src/modules/common/services/usergroups.service.ts +++ b/src/modules/common/services/usergroups.service.ts @@ -1,19 +1,20 @@ import { Component, BadRequestException } from "@nestjs/common"; import { Model as UsersModel } from "@models/User"; -import { Model as UsergroupsModel } from "@models/Usergroup"; +import { Model as UsergroupsModel, IUsergroups } from "@models/Usergroup"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { ObjectId } from "@models/common"; import { BaseService } from "@services/base"; @Component() -export class UsergroupsService extends BaseService { +export class UsergroupsService extends BaseService { - public async add(obj: object) { - try { - return await UsergroupsModel.create(obj); - } catch (error) { - throw new BadRequestException(error.toString()); - } + constructor() { + super(); + this.setModel(UsergroupsModel); + } + + public add(obj: object) { + return this.create(obj); } public async edit(id: ObjectId, obj: object) { @@ -31,7 +32,7 @@ export class UsergroupsService extends BaseService { } public getGroup(gid: ObjectId) { - return UsergroupsModel.findById(gid).exec(); + return this.findById(gid); } public async getGroupUsers( @@ -48,16 +49,18 @@ export class UsergroupsService extends BaseService { } public count() { - return UsergroupsModel.count({ }).exec(); + return this.total({ }); } public list(pageObj = this.DEF_PER_OBJ) { const perNum = pageObj.perNum || this.DEF_PER_OBJ.perNum; const page = pageObj.page || this.DEF_PER_OBJ.page; - return UsergroupsModel.find({ }) - .skip((page - 1) * perNum).limit(perNum) - .sort({ createdAt: -1 }) - .exec(); + return this.loadAndCache( + `list_${perNum}_${page}`, + () => this.findObjects({ }, { + perNum, page, sort: { createdAt: -1 } + }) + ); } /** @@ -68,8 +71,8 @@ export class UsergroupsService extends BaseService { if ((await this.count()) === 1) { throw new BadRequestException("Cant delete unique group"); } + const p = await this.deleteById(gid); try { - const p = await UsergroupsModel.findByIdAndRemove(gid).exec(); await UserUsergroupsModel.findOneAndRemove({ usergroup: gid }).exec(); diff --git a/src/modules/common/services/users.service.ts b/src/modules/common/services/users.service.ts index ba03ca0..52bcd24 100644 --- a/src/modules/common/services/users.service.ts +++ b/src/modules/common/services/users.service.ts @@ -1,16 +1,18 @@ import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; -import { Model as UsersModel, UserDoc } from "@models/User"; +import { Model as UsersModel, UserDoc, IUser, cache } from "@models/User"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { IUsergroups } from "@models/Usergroup"; import { SystemService } from "@services/system"; import { BaseService, IGetOptions } from "@services/base"; @Component() -export class UsersService extends BaseService { +export class UsersService extends BaseService { constructor(private readonly sysSvr: SystemService) { super(); + this.setCache(cache); + this.setModel(UsersModel); } public async addUser(obj, gid?: ObjectId) { @@ -29,8 +31,8 @@ export class UsersService extends BaseService { } public async removeUser(uid: ObjectId) { + await this.deleteById(uid); try { - await UsersModel.removeUser(uid); await UserUsergroupsModel.remove({ user: uid }).exec(); } catch (error) { throw new BadRequestException(error.toString()); @@ -52,8 +54,8 @@ export class UsersService extends BaseService { public async getUsergroups( uid: ObjectId, pageObj = this.DEF_PER_OBJ ) { - const perNum = pageObj.perNum; - const page = pageObj.page; + const perNum = pageObj.perNum || this.DEF_PER_OBJ.perNum; + const page = pageObj.page || this.DEF_PER_OBJ.page; const groups = await UserUsergroupsModel .find({ user: uid }).populate("usergroup") .skip((page - 1) * perNum).limit(perNum) @@ -77,13 +79,7 @@ export class UsersService extends BaseService { if (Object.keys(content).length === 0) { throw new BadRequestException("Empty Content"); } - try { - return await UsersModel - .update({ _id: id }, content, this.DEF_UPDATE_OPTIONS) - .exec(); - } catch (error) { - throw new BadRequestException(error.toString()); - } + return this.modifyById(id, content); } public async passwd(id: ObjectId, oldPass: string, newPass: string) { @@ -98,10 +94,7 @@ export class UsersService extends BaseService { * 返回总数 */ public conut() { - return this.loadAndCache( - "count", - () => UsersModel.count({ }).exec() - ); + return this.total({ }); } /** @@ -111,12 +104,15 @@ export class UsersService extends BaseService { * @param opts.page {number} 页数 */ public list(opts = this.DEF_PER_OBJ) { - const Flag = `list_${opts.perNum}_${opts.page}`; + const perNum = opts.perNum || this.DEF_PER_OBJ.perNum; + const page = opts.page || this.DEF_PER_OBJ.page; + const Flag = `list_${perNum}_${page}`; return this.loadAndCache( Flag, - () => UsersModel.find().select("-password") - .skip((opts.page - 1) * opts.perNum).limit(opts.perNum) - .exec() + () => this.findObjects({ }, { + select: "-password", + perNum, page + }) ); } @@ -125,8 +121,6 @@ export class UsersService extends BaseService { * @param id User ID */ public getById(id: ObjectId, opts?: IGetOptions) { - let p = UsersModel.findById(id); - p = this.documentQueryProcess(p, opts); - return p.exec(); + return this.findById(id, opts); } } From 9bf014c77c7c4803d0e7ea3c9554872aa40f1c09 Mon Sep 17 00:00:00 2001 From: Arylo Date: Wed, 21 Mar 2018 16:03:28 +0800 Subject: [PATCH 41/49] Move Origin Url to boxsystem/storebox-api --- README.md | 8 ++++---- package.json | 2 +- test/issues/github/github_issue_16.e2e.ts | 2 +- test/issues/github/github_issue_21.e2e.ts | 2 +- test/issues/github/github_issue_22.e2e.ts | 2 +- test/issues/github/github_issue_27.e2e.ts | 2 +- test/issues/github/github_issue_28.e2e.ts | 2 +- test/issues/github/github_issue_30.spec.ts | 2 +- test/issues/github/github_issue_31.e2e.ts | 2 +- test/issues/github/github_issue_35.e2e.ts | 2 +- test/issues/gitlab/issue_1.e2e.ts | 2 +- test/issues/gitlab/issue_2.e2e.ts | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index a2eb387..ff1068c 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Store PandoraBox Firmware Images and its packages -[![Travis](https://img.shields.io/travis/Arylo/StoreBox.svg?style=flat-square)](https://travis-ci.org/Arylo/StoreBox) -[![Coveralls](https://img.shields.io/coveralls/github/Arylo/StoreBox.svg?style=flat-square)](https://coveralls.io/github/Arylo/StoreBox) -[![Known Vulnerabilities](https://snyk.io/test/github/Arylo/StoreBox/badge.svg?style=flat-square)](https://snyk.io/test/github/Arylo/StoreBox) -[![license](https://img.shields.io/github/license/Arylo/StoreBox.svg?style=flat-square)](https://github.com/Arylo/storebox) +[![Travis](https://img.shields.io/travis/BoxSystem/StoreBox-Api.svg?style=flat-square)](https://travis-ci.org/BoxSystem/StoreBox-Api) +[![Coveralls](https://img.shields.io/coveralls/github/BoxSystem/StoreBox-Api.svg?style=flat-square)](https://coveralls.io/github/BoxSystem/StoreBox-Api) +[![Known Vulnerabilities](https://snyk.io/test/github/BoxSystem/StoreBox-Api/badge.svg?style=flat-square)](https://snyk.io/test/github/BoxSystem/StoreBox-Api) +[![license](https://img.shields.io/github/license/BoxSystem/StoreBox-Api.svg?style=flat-square)](https://github.com/BoxSystem/StoreBox-Api) # Usage diff --git a/package.json b/package.json index 3020418..cb6c29f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "storebox", + "name": "storebox-api", "version": "1.2.0", "description": "", "main": "index.js", diff --git a/test/issues/github/github_issue_16.e2e.ts b/test/issues/github/github_issue_16.e2e.ts index ff5c9a1..5035f43 100644 --- a/test/issues/github/github_issue_16.e2e.ts +++ b/test/issues/github/github_issue_16.e2e.ts @@ -7,7 +7,7 @@ import { init } from "../../helpers/server"; import auth = require("@db/auth"); /** - * Fix [Issue 16](https://github.com/Arylo/StoreBox/issues/16) + * Fix [Issue 16](https://github.com/BoxSystem/StoreBox-Api/issues/16) */ describe("Fix Issues", () => { diff --git a/test/issues/github/github_issue_21.e2e.ts b/test/issues/github/github_issue_21.e2e.ts index 7934d27..50280db 100644 --- a/test/issues/github/github_issue_21.e2e.ts +++ b/test/issues/github/github_issue_21.e2e.ts @@ -8,7 +8,7 @@ import auth = require("@db/auth"); import { newName } from "../../helpers/utils"; /** - * About [Issue 21](https://github.com/Arylo/StoreBox/issues/21) + * About [Issue 21](https://github.com/BoxSystem/StoreBox-Api/issues/21) */ describe("Fix Issues", () => { diff --git a/test/issues/github/github_issue_22.e2e.ts b/test/issues/github/github_issue_22.e2e.ts index d4e3ba2..c0997f0 100644 --- a/test/issues/github/github_issue_22.e2e.ts +++ b/test/issues/github/github_issue_22.e2e.ts @@ -8,7 +8,7 @@ import auth = require("@db/auth"); import { newName } from "../../helpers/utils"; /** - * Fix [Issue 22](https://github.com/Arylo/StoreBox/issues/22) + * Fix [Issue 22](https://github.com/BoxSystem/StoreBox-Api/issues/22) */ describe("Fix Issues", () => { diff --git a/test/issues/github/github_issue_27.e2e.ts b/test/issues/github/github_issue_27.e2e.ts index 59f37c8..232ee18 100644 --- a/test/issues/github/github_issue_27.e2e.ts +++ b/test/issues/github/github_issue_27.e2e.ts @@ -8,7 +8,7 @@ import auth = require("@db/auth"); import { newName } from "../../helpers/utils"; /** - * Fix [Issue 27](https://github.com/Arylo/StoreBox/issues/27) + * Fix [Issue 27](https://github.com/BoxSystem/StoreBox-Api/issues/27) */ describe("Fix Issues", () => { diff --git a/test/issues/github/github_issue_28.e2e.ts b/test/issues/github/github_issue_28.e2e.ts index e509c04..05158f2 100644 --- a/test/issues/github/github_issue_28.e2e.ts +++ b/test/issues/github/github_issue_28.e2e.ts @@ -7,7 +7,7 @@ import { init } from "../../helpers/server"; import auth = require("@db/auth"); /** - * Fix [Issue 28](https://github.com/Arylo/StoreBox/issues/28) + * Fix [Issue 28](https://github.com/BoxSystem/StoreBox-Api/issues/28) */ describe("Fix Issues", () => { diff --git a/test/issues/github/github_issue_30.spec.ts b/test/issues/github/github_issue_30.spec.ts index 52459d5..75f908f 100644 --- a/test/issues/github/github_issue_30.spec.ts +++ b/test/issues/github/github_issue_30.spec.ts @@ -4,7 +4,7 @@ import { Model as RegexpsModel } from "@models/Regexp"; import { newName } from "../../helpers/utils"; /** - * Fix [Issue 30](https://github.com/Arylo/StoreBox/issues/30) + * Fix [Issue 30](https://github.com/BoxSystem/StoreBox-Api/issues/30) */ describe("Fix Issues", () => { diff --git a/test/issues/github/github_issue_31.e2e.ts b/test/issues/github/github_issue_31.e2e.ts index b690e5f..af6ca86 100644 --- a/test/issues/github/github_issue_31.e2e.ts +++ b/test/issues/github/github_issue_31.e2e.ts @@ -13,7 +13,7 @@ import { config } from "@utils/config"; import auth = require("@db/auth"); /** - * Fix [Issue 31](https://github.com/Arylo/StoreBox/issues/31) + * Fix [Issue 31](https://github.com/BoxSystem/StoreBox-Api/issues/31) */ describe("Fix Issues", () => { diff --git a/test/issues/github/github_issue_35.e2e.ts b/test/issues/github/github_issue_35.e2e.ts index 79254d7..ae61507 100644 --- a/test/issues/github/github_issue_35.e2e.ts +++ b/test/issues/github/github_issue_35.e2e.ts @@ -9,7 +9,7 @@ import { newName } from "../../helpers/utils"; /** * The Feature of Edit User - * Fix [Issue 35](https://github.com/Arylo/StoreBox/issues/35) + * Fix [Issue 35](https://github.com/BoxSystem/StoreBox-Api/issues/35) */ describe("Fix Issues", () => { diff --git a/test/issues/gitlab/issue_1.e2e.ts b/test/issues/gitlab/issue_1.e2e.ts index 9ce4e46..6aa38dd 100644 --- a/test/issues/gitlab/issue_1.e2e.ts +++ b/test/issues/gitlab/issue_1.e2e.ts @@ -8,7 +8,7 @@ import * as files from "../../helpers/files"; import goods = require("@db/goods"); /** - * Fix [Issue 1](http://git.pbr.link/BoxSystem/StoreBox/issues/1) + * Fix [Issue 1](http://git.pbr.link/Arylo/StoreBox/issues/1) */ describe("Fix Issues", () => { diff --git a/test/issues/gitlab/issue_2.e2e.ts b/test/issues/gitlab/issue_2.e2e.ts index 8f4d283..f4805b8 100644 --- a/test/issues/gitlab/issue_2.e2e.ts +++ b/test/issues/gitlab/issue_2.e2e.ts @@ -6,7 +6,7 @@ import * as server from "../../helpers/server"; import { newName } from "../../helpers/utils"; /** - * Fix [Issue 2](http://git.pbr.link/BoxSystem/StoreBox/issues/2) + * Fix [Issue 2](http://git.pbr.link/Arylo/StoreBox/issues/2) */ describe("Fix Issues", () => { From 077bb1e274a9175bbc6b161f4a9dbd05c7003dad Mon Sep 17 00:00:00 2001 From: Arylo Date: Sun, 25 Mar 2018 18:53:05 +0800 Subject: [PATCH 42/49] Add System module --- package.json | 2 +- src/models/System.ts | 17 +- src/modules/common/services/base.service.ts | 46 ++++- .../common/services/regexps.service.ts | 15 +- src/modules/common/services/system.service.ts | 165 +++++++++++++++--- src/modules/database/database.providers.ts | 6 +- src/modules/system/system.controller.ts | 36 ++++ src/modules/system/system.dto.ts | 12 ++ src/modules/users/auth.controller.ts | 2 +- 9 files changed, 258 insertions(+), 43 deletions(-) create mode 100644 src/modules/system/system.controller.ts create mode 100644 src/modules/system/system.dto.ts diff --git a/package.json b/package.json index cb6c29f..fdf1aee 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "clean": "rimraf dist tmp", - "tsc": "tsc -P .", + "tsc": "tsc --locale zh-cn --pretty -P .", "build": "npm run clean && npm run tsc", "prestart": "npm run build", "start-svr": "node -r tsconfig-paths/register index.js", diff --git a/src/models/System.ts b/src/models/System.ts index 375b703..531f31c 100644 --- a/src/models/System.ts +++ b/src/models/System.ts @@ -1,5 +1,14 @@ import { model, SchemaDefinition, Model as M } from "mongoose"; -import { Base, IDoc, IDocRaw } from "./common"; +import { Base, IDoc, IDocRaw, MODIFY_MOTHODS } from "./common"; +import { config } from "@utils/config"; +import keyv = require("keyv"); + +import { isTest } from "../modules/common/helper/env"; + +export const cache = new keyv({ + uri: isTest ? undefined : config.redis.url, + namespace: "Categories" +}); const Definition: SchemaDefinition = { key: { type: String, required: true }, @@ -17,4 +26,10 @@ export const Flag = "sys"; export type SystemDoc = IDoc; +for (const method of MODIFY_MOTHODS) { + SystemSchema.post(method, () => { + cache.clear(); + }); +} + export const Model: M = model(Flag, SystemSchema); diff --git a/src/modules/common/services/base.service.ts b/src/modules/common/services/base.service.ts index 84f2e28..1bcb9fc 100644 --- a/src/modules/common/services/base.service.ts +++ b/src/modules/common/services/base.service.ts @@ -34,8 +34,26 @@ abstract class ModelService { } } + protected runBeforeAll() { + return Promise.resolve(this.beforeAll()); + } + + protected beforeAll(): Promise { + return Promise.resolve({ }); + } + + private runBeforeEach() { + return Promise.resolve(this.beforeEach()); + } + + protected beforeEach(): Promise { + return Promise.resolve({ }); + } + public async create(obj: object) { this.checkModel(); + await this.beforeAll(); + await this.beforeEach(); try { return await this.model.create(obj); } catch (error) { @@ -45,6 +63,8 @@ abstract class ModelService { public async delete(cond: object) { this.checkModel(); + await this.beforeAll(); + await this.beforeEach(); try { return await this.model.findOneAndRemove(cond).exec(); } catch (error) { @@ -54,6 +74,8 @@ abstract class ModelService { public async deleteById(id: ObjectId) { this.checkModel(); + await this.beforeAll(); + await this.beforeEach(); try { return await this.model.findByIdAndRemove(id).exec(); } catch (error) { @@ -65,6 +87,8 @@ abstract class ModelService { id: ObjectId, ctx: object, opts = this.DEF_UPDATE_OPTIONS ) { this.checkModel(); + await this.beforeAll(); + await this.beforeEach(); const options = Object.assign({ }, this.DEF_UPDATE_OPTIONS, opts); try { return await this.model.update({ _id: id }, ctx, options).exec(); @@ -73,8 +97,10 @@ abstract class ModelService { } } - protected find(cond: object, opts?: IGetOptions) { + protected async find(cond: object, opts?: IGetOptions) { this.checkModel(); + await this.beforeAll(); + await this.beforeEach(); const p = this.model.find(cond); return this.documentQueryProcess(p, opts).exec(); } @@ -85,8 +111,10 @@ abstract class ModelService { }); } - protected findOne(cond: object, opts?: IGetOptions) { + protected async findOne(cond: object, opts?: IGetOptions) { this.checkModel(); + await this.beforeAll(); + await this.beforeEach(); const p = this.model.findOne(cond); return this.documentQueryProcess(p, opts).exec(); } @@ -97,8 +125,10 @@ abstract class ModelService { }); } - protected findById(id: ObjectId, opts?: IGetOptions) { + protected async findById(id: ObjectId, opts?: IGetOptions) { this.checkModel(); + await this.beforeAll(); + await this.beforeEach(); const p = this.model.findById(id); return this.documentQueryProcess(p, opts).exec(); } @@ -109,8 +139,10 @@ abstract class ModelService { }); } - protected total(cond: object = { }) { + protected async total(cond: object = { }) { this.checkModel(); + await this.beforeAll(); + await this.beforeEach(); return this.model.count(cond).exec(); } @@ -170,6 +202,12 @@ export abstract class BaseService extends ModelServ return val; } + protected runBeforeAll() { + return this.loadAndCache("_RunBeforeAll_", () => { + return super.runBeforeAll(); + }); + } + protected DEF_PER_OBJ = UtilService.DEF_PER_OBJ; /** diff --git a/src/modules/common/services/regexps.service.ts b/src/modules/common/services/regexps.service.ts index cf4d9f8..6d546e9 100644 --- a/src/modules/common/services/regexps.service.ts +++ b/src/modules/common/services/regexps.service.ts @@ -20,14 +20,15 @@ export class RegexpsService extends BaseService { super(); this.setCache(cache); this.setModel(RegexpsModel); + } + + protected async beforeAll() { // Update - setTimeout(() => { - // Add Hidden Label - RegexpsModel.update( - { hidden: { $exists: false } }, { hidden: false }, - { multi: true } - ).exec(); - }, 3000); + // Add Hidden Label + await RegexpsModel.update( + { hidden: { $exists: false } }, { hidden: false }, + { multi: true } + ).exec(); } /** diff --git a/src/modules/common/services/system.service.ts b/src/modules/common/services/system.service.ts index 5553f8f..857ecbc 100644 --- a/src/modules/common/services/system.service.ts +++ b/src/modules/common/services/system.service.ts @@ -1,30 +1,72 @@ import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; -import { Model as SystemModel } from "@models/System"; +import { Model as SystemModel, cache, ISystem } from "@models/System"; import { Model as UsergroupsModel } from "@models/Usergroup"; +import { BaseService, IGetOptions } from "@services/base"; +import { isURL } from "validator"; +import * as typescript from "typescript"; import { systemLogger } from "../helper/log"; +export enum DEFAULTS { + USERGROUP_FLAG = "DEFAULT_USERGROUP", + GOOD_URL_FLAG = "DEFAULT_GOOD_URL", + COLLECTION_URL_FLAG = "DEFAULT_COLLECTION_URL" +} + @Component() -export class SystemService { +export class SystemService extends BaseService { - public static DEFAULT_USERGROUP_FLAG = "DEFAULT_USERGROUP"; + constructor() { + super(); + super.setCache(cache); + super.setModel(SystemModel); + } + + private async checkUsergroupId(gid: ObjectId) { + const doc = await UsergroupsModel.findById(gid).exec(); + /* istanbul ignore if */ + if (!doc) { + throw new BadRequestException("The ID isnt a Usergroup ID"); + } + return true; + } + private async checkUrl(url: string) { + if (!isURL(url)) { + throw new BadRequestException("URL Parse Fail"); + } + return true; + } + + private setValue(key: string, value: string) { + try { + return SystemModel.findOneAndUpdate( + { key: key }, { value: value }, { upsert: true } + ).exec(); + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + // region Default Usergroup ID /** * Get Default Usergroup ID * @returns Usergroup ID */ - public async getDefaultUsergroup(): Promise { - let gid: any = await SystemModel.findOne({ - key: SystemService.DEFAULT_USERGROUP_FLAG - }).exec(); - /* istanbul ignore if */ - if (!gid) { - systemLogger.warn(`Miss ${SystemService.DEFAULT_USERGROUP_FLAG}`); - gid = (await UsergroupsModel.findOne().exec())._id; - this.setDefaultUsergroup(gid); - } - return gid; + public getDefaultUsergroup() { + const FLAG = DEFAULTS.USERGROUP_FLAG; + return this.loadAndCache(FLAG, async () => { + const obj = await this.findObject({ key: FLAG }); + let gid = obj.value; + /* istanbul ignore if */ + if (!gid) { + systemLogger.warn(`Miss ${FLAG}`); + gid = (await UsergroupsModel.findOne().exec())._id; + this.setDefaultUsergroup(gid); + } + return gid; + }); } /** @@ -32,17 +74,92 @@ export class SystemService { * @param gid Usergroup ID */ public async setDefaultUsergroup(gid: ObjectId) { - const doc = await UsergroupsModel.findById(gid).exec(); - /* istanbul ignore if */ - if (!doc) { - throw new BadRequestException("The ID isnt a Usergroup ID"); + await this.checkUsergroupId(gid); + return await this.setValue(DEFAULTS.USERGROUP_FLAG, gid.toString()); + } + // endregion Default Usergroup ID + + // region Default Urls + public getDefaultGoodUrl() { + const FLAG = DEFAULTS.GOOD_URL_FLAG; + return this.loadAndCache(FLAG, async () => { + const obj = await this.findObject({ key: FLAG }); + return obj ? obj.value : ""; + }); + } + + public async setDefaultGoodUrl(url: string) { + if (url.length !== 0) { + if (!url.includes("{{gid}}")) { + throw new BadRequestException("Url must include `{{gid}}`"); + } + this.checkUrl(url); } - return SystemModel - .findOneAndUpdate( - { key: SystemService.DEFAULT_USERGROUP_FLAG }, { value: gid }, - { upsert: true } - ) - .exec(); + return await this.setValue(DEFAULTS.GOOD_URL_FLAG, url); + } + + public getDefaultCollectionUrl() { + const FLAG = DEFAULTS.COLLECTION_URL_FLAG; + return this.loadAndCache(FLAG, async () => { + const obj = await this.findObject({ key: FLAG }); + return obj ? obj.value : ""; + }); + } + + public async setDefaultCollectionUrl(url: string) { + if (url.length !== 0) { + if (!url.includes("{{cid}}")) { + throw new BadRequestException("Url must include `{{cid}}`"); + } + this.checkUrl(url); + } + return await this.setValue(DEFAULTS.COLLECTION_URL_FLAG, url); + } + // endregion Default Urls + + public get() { + return this.loadAndCache("get", async () => { + const objs = await this.findObjects({ }, { select: "key value" }); + const keys = objs.reduce((arr, item) => { + arr.push(item.key); + return arr; + }, [ ]); + objs.push(...Object.keys(DEFAULTS).reduce((arr, key) => { + if (!~keys.indexOf(key)) { + arr.push({ + key, value: "" + }); + } + return arr; + }, [ ]) as any[]); + return objs; + }); + } + + public set(key: DEFAULTS, value: string) { + switch (key) { + case DEFAULTS.USERGROUP_FLAG: + return this.setDefaultUsergroup(value); + case DEFAULTS.GOOD_URL_FLAG: + return this.setDefaultGoodUrl(value); + case DEFAULTS.COLLECTION_URL_FLAG: + return this.setDefaultCollectionUrl(value); + } + throw new BadRequestException("What do you want to set up"); + } + + public async info() { + return { + version: { + typescript: typescript.version, + api: require("../../../../package.json").version, + node: process.versions + }, + env: { + system: process.env, + service: await this.get() + } + }; } } diff --git a/src/modules/database/database.providers.ts b/src/modules/database/database.providers.ts index 5b40776..d03a91a 100644 --- a/src/modules/database/database.providers.ts +++ b/src/modules/database/database.providers.ts @@ -4,7 +4,6 @@ import { config } from "@utils/config"; import { Model as UsersModel } from "@models/User"; import { Model as UsergroupsModel } from "@models/Usergroup"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; -import { Model as SystemModel } from "@models/System"; import { SystemService } from "@services/system"; import { systemLogger } from "../common/helper/log"; @@ -55,10 +54,7 @@ export const injectData = async () => { usergroup: group._id }; }); - await SystemModel.findOneAndUpdate( - { key: SystemService.DEFAULT_USERGROUP_FLAG }, - { value: group._id.toString() }, { upsert: true } - ).exec(); + await new SystemService().setDefaultUsergroup(group._id); await UserUsergroupsModel.create(conditions); } }; diff --git a/src/modules/system/system.controller.ts b/src/modules/system/system.controller.ts new file mode 100644 index 0000000..4a71984 --- /dev/null +++ b/src/modules/system/system.controller.ts @@ -0,0 +1,36 @@ +import { Controller, Get, UseGuards, Put, Body } from "@nestjs/common"; +import { SystemService } from "@services/system"; +import { RolesGuard } from "@guards/roles"; +import { ApiUseTags, ApiBearerAuth } from "@nestjs/swagger"; +import { Roles } from "@decorators/roles"; +import { EditSystemVarDto } from "./system.dto"; + +@UseGuards(RolesGuard) +@Controller("api/v1/system") +// region Swagger Docs +@ApiUseTags("System") +@ApiBearerAuth() +// endregion Swagger Docs +export class SystemController { + + constructor(private readonly systemSvr: SystemService) { } + + @Roles("guest") + @Get("/vars") + public getVars() { + return this.systemSvr.get(); + } + + @Roles("admin") + @Put("/vars") + public setVars(@Body() body: EditSystemVarDto) { + return this.systemSvr.set(body.key, body.value); + } + + @Roles("admin") + @Get("/info") + public info() { + return this.systemSvr.info(); + } + +} diff --git a/src/modules/system/system.dto.ts b/src/modules/system/system.dto.ts new file mode 100644 index 0000000..ef3898c --- /dev/null +++ b/src/modules/system/system.dto.ts @@ -0,0 +1,12 @@ +import { ApiModelProperty } from "@nestjs/swagger"; +import { IsString } from "class-validator"; +import { DEFAULTS } from "@services/system"; + +export class EditSystemVarDto { + @ApiModelProperty({ type: String }) + @IsString() + public readonly key: DEFAULTS; + @ApiModelProperty({ type: String }) + @IsString() + public readonly value: string; +} diff --git a/src/modules/users/auth.controller.ts b/src/modules/users/auth.controller.ts index e89a98d..629e946 100644 --- a/src/modules/users/auth.controller.ts +++ b/src/modules/users/auth.controller.ts @@ -34,7 +34,7 @@ export class AuthAdminController { @Body() ctx: LoginBodyDto, @Query() query: LoginQueryDto ) { const user = - await this.usersSvr.isVaild(ctx.username, ctx.password);; + await this.usersSvr.isVaild(ctx.username, ctx.password); session.loginUser = user.toObject().username; session.loginUserId = user.toObject()._id; const obj = new LoginRespone(); From 94a92fc9c65deaec20f26b3f9e40a7fe4ecdf322 Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Mar 2018 03:39:56 +0800 Subject: [PATCH 43/49] Add Test Unit --- src/modules/common/services/system.service.ts | 52 ++++++--- src/modules/controllers.module.ts | 4 +- src/modules/system/system.controller.ts | 8 +- test/api/system/system.e2e.ts | 108 ++++++++++++++++++ 4 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 test/api/system/system.e2e.ts diff --git a/src/modules/common/services/system.service.ts b/src/modules/common/services/system.service.ts index 857ecbc..10a50ec 100644 --- a/src/modules/common/services/system.service.ts +++ b/src/modules/common/services/system.service.ts @@ -23,6 +23,21 @@ export class SystemService extends BaseService { super.setModel(SystemModel); } + private readonly DEFAULTS_MAPS = { + [DEFAULTS.USERGROUP_FLAG]: { + get: this.getDefaultUsergroup.name, + set: this.setDefaultUsergroup.name + }, + [DEFAULTS.GOOD_URL_FLAG]: { + get: this.getDefaultGoodUrl.name, + set: this.setDefaultGoodUrl.name + }, + [DEFAULTS.COLLECTION_URL_FLAG]: { + get: this.getDefaultCollectionUrl.name, + set: this.setDefaultCollectionUrl.name + } + }; + private async checkUsergroupId(gid: ObjectId) { const doc = await UsergroupsModel.findById(gid).exec(); /* istanbul ignore if */ @@ -88,14 +103,14 @@ export class SystemService extends BaseService { }); } - public async setDefaultGoodUrl(url: string) { + public setDefaultGoodUrl(url: string) { if (url.length !== 0) { if (!url.includes("{{gid}}")) { throw new BadRequestException("Url must include `{{gid}}`"); } this.checkUrl(url); } - return await this.setValue(DEFAULTS.GOOD_URL_FLAG, url); + return this.setValue(DEFAULTS.GOOD_URL_FLAG, url); } public getDefaultCollectionUrl() { @@ -106,44 +121,49 @@ export class SystemService extends BaseService { }); } - public async setDefaultCollectionUrl(url: string) { + public setDefaultCollectionUrl(url: string) { if (url.length !== 0) { if (!url.includes("{{cid}}")) { throw new BadRequestException("Url must include `{{cid}}`"); } this.checkUrl(url); } - return await this.setValue(DEFAULTS.COLLECTION_URL_FLAG, url); + return this.setValue(DEFAULTS.COLLECTION_URL_FLAG, url); } // endregion Default Urls public get() { return this.loadAndCache("get", async () => { - const objs = await this.findObjects({ }, { select: "key value" }); + const objs: Array<{ key: string, value: string }> = + await this.findObjects({ }, { select: "key value -_id" }); const keys = objs.reduce((arr, item) => { arr.push(item.key); return arr; }, [ ]); - objs.push(...Object.keys(DEFAULTS).reduce((arr, key) => { - if (!~keys.indexOf(key)) { - arr.push({ - key, value: "" - }); + if (objs.length === Object.keys(DEFAULTS).length) { + return objs; + } + for (const k of Object.keys(DEFAULTS)) { + if (!!~keys.indexOf(DEFAULTS[k])) { + continue; } - return arr; - }, [ ]) as any[]); + const key = DEFAULTS[k]; + objs.push({ + key, value: await this[this.DEFAULTS_MAPS[key].get]() + }); + } return objs; }); } - public set(key: DEFAULTS, value: string) { + public set(key: DEFAULTS, value: any) { switch (key) { case DEFAULTS.USERGROUP_FLAG: - return this.setDefaultUsergroup(value); + return this[this.DEFAULTS_MAPS[key].set](value); case DEFAULTS.GOOD_URL_FLAG: - return this.setDefaultGoodUrl(value); + return this[this.DEFAULTS_MAPS[key].set](value); case DEFAULTS.COLLECTION_URL_FLAG: - return this.setDefaultCollectionUrl(value); + return this[this.DEFAULTS_MAPS[key].set](value); } throw new BadRequestException("What do you want to set up"); } diff --git a/src/modules/controllers.module.ts b/src/modules/controllers.module.ts index b383edd..cb817d1 100644 --- a/src/modules/controllers.module.ts +++ b/src/modules/controllers.module.ts @@ -15,6 +15,7 @@ import { } from "./collections/collections.admin.controller"; import { TokensAdminController } from "./tokens/tokens.controller"; import { UsergroupsAdminController } from "./usergroups/usergroups.controller"; +import { SystemController } from "./system/system.controller"; // endregion Controllers // region Middlewares @@ -48,7 +49,8 @@ export const controllers = [ RegexpsAdminController, CategoriesAdminController, GoodsAdminController, TokensAdminController, - CollectionsController, CollectionsAdminController + CollectionsController, CollectionsAdminController, + SystemController ]; export const services = [ diff --git a/src/modules/system/system.controller.ts b/src/modules/system/system.controller.ts index 4a71984..47bbe86 100644 --- a/src/modules/system/system.controller.ts +++ b/src/modules/system/system.controller.ts @@ -1,3 +1,4 @@ +import { DefResDto } from "@dtos/res"; import { Controller, Get, UseGuards, Put, Body } from "@nestjs/common"; import { SystemService } from "@services/system"; import { RolesGuard } from "@guards/roles"; @@ -15,7 +16,7 @@ export class SystemController { constructor(private readonly systemSvr: SystemService) { } - @Roles("guest") + @Roles("admin", "token") @Get("/vars") public getVars() { return this.systemSvr.get(); @@ -23,8 +24,9 @@ export class SystemController { @Roles("admin") @Put("/vars") - public setVars(@Body() body: EditSystemVarDto) { - return this.systemSvr.set(body.key, body.value); + public async setVars(@Body() body: EditSystemVarDto) { + await this.systemSvr.set(body.key, body.value); + return new DefResDto(); } @Roles("admin") diff --git a/test/api/system/system.e2e.ts b/test/api/system/system.e2e.ts new file mode 100644 index 0000000..f93c07b --- /dev/null +++ b/test/api/system/system.e2e.ts @@ -0,0 +1,108 @@ +import { Model as SystemModel } from "@models/System"; +import supertest = require("supertest"); +import { + connect, drop +} from "../../helpers/database"; +import { init } from "../../helpers/server"; +import auth = require("@db/auth"); +import { newName } from "../../helpers/utils"; + +describe("Collections E2E Api", () => { + + let request: supertest.SuperTest; + + before(() => { + return connect(); + }); + + const ids = { + users: [ ] + }; + + after(() => { + return drop(ids); + }); + + before(async () => { + request = await init(); + }); + + before("login", async () => { + ids.users.push((await auth.login(request))[0]); + }); + + after(async () => { + await SystemModel + .findOneAndRemove({ key: "DEFAULT_GOOD_URL" }) + .exec(); + await SystemModel + .findOneAndRemove({ key: "DEFAULT_COLLECTION_URL" }) + .exec(); + }); + + it("Get System Info", async () => { + const { status } = await request.get("/api/v1/system/info").then(); + status.should.be.eql(200); + }); + + it("Set Good Url", async () => { + const value = `http://example.com/${newName()}/{{gid}}`; + const { status } = await request.put("/api/v1/system/vars") + .send({ key: "DEFAULT_GOOD_URL", value }).then(); + status.should.be.eql(200); + const { body } = await request.get("/api/v1/system/vars").then(); + body.should.matchAny({ key: "DEFAULT_GOOD_URL", value }); + }); + + it("Set Empty Good Url", async () => { + const value = ""; + const { status } = await request.put("/api/v1/system/vars") + .send({ key: "DEFAULT_GOOD_URL", value }).then(); + status.should.be.eql(200); + const { body } = await request.get("/api/v1/system/vars").then(); + body.should.matchAny({ key: "DEFAULT_GOOD_URL", value }); + }); + + it("Set one wrong Good Url", async () => { + const value = `http://example.com/${newName()}`; + const { status } = await request.put("/api/v1/system/vars") + .send({ key: "DEFAULT_GOOD_URL", value }).then(); + status.should.be.eql(400); + const { body } = await request.get("/api/v1/system/vars").then(); + body.should.not.matchAny({ key: "DEFAULT_GOOD_URL", value }); + }); + + it("Set Collection Url", async () => { + const value = `http://example.com/${newName()}/{{cid}}`; + const { status } = await request.put("/api/v1/system/vars") + .send({ key: "DEFAULT_COLLECTION_URL", value }).then(); + status.should.be.eql(200); + const { body } = await request.get("/api/v1/system/vars").then(); + body.should.matchAny({ key: "DEFAULT_COLLECTION_URL", value }); + }); + + it("Set Empty Collection Url", async () => { + const value = ""; + const { status } = await request.put("/api/v1/system/vars") + .send({ key: "DEFAULT_COLLECTION_URL", value }).then(); + status.should.be.eql(200); + const { body } = await request.get("/api/v1/system/vars").then(); + body.should.matchAny({ key: "DEFAULT_COLLECTION_URL", value }); + }); + + it("Set one wrong Collection Url", async () => { + const value = `http://example.com/${newName()}`; + const { status } = await request.put("/api/v1/system/vars") + .send({ key: "DEFAULT_COLLECTION_URL", value }).then(); + status.should.be.eql(400); + const { body } = await request.get("/api/v1/system/vars").then(); + body.should.not.matchAny({ key: "DEFAULT_COLLECTION_URL", value }); + }); + + it("set value to non-exist key", async () => { + const { status } = await request.put("/api/v1/system/vars") + .send({ key: newName(), value: newName() }).then(); + status.should.be.eql(400); + }); + +}); From f525cba1f7a9ee5b1b960dfd48303cd3d3ef1ccc Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Mar 2018 03:44:15 +0800 Subject: [PATCH 44/49] Fix Cache Namespace name --- src/models/System.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/System.ts b/src/models/System.ts index 531f31c..80eb992 100644 --- a/src/models/System.ts +++ b/src/models/System.ts @@ -7,7 +7,7 @@ import { isTest } from "../modules/common/helper/env"; export const cache = new keyv({ uri: isTest ? undefined : config.redis.url, - namespace: "Categories" + namespace: "System" }); const Definition: SchemaDefinition = { @@ -22,7 +22,7 @@ export interface ISystem extends IDocRaw { const SystemSchema = new Base(Definition).createSchema(); -export const Flag = "sys"; +export const Flag = "system"; export type SystemDoc = IDoc; From 1cdffb2b0f2582a189fd933676ae4c942d8f61c4 Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Mar 2018 04:32:57 +0800 Subject: [PATCH 45/49] Add new function of ids for test --- test/api/categories/categories.e2e.ts | 7 ++----- test/api/collections/collections.e2e.ts | 10 ++-------- test/api/collections/collections_token.e2e.ts | 9 ++------- test/api/files.e2e.ts | 9 ++------- test/api/goods/goods.e2e.ts | 10 ++-------- test/api/goods/goods_append.e2e.ts | 10 +++------- test/api/goods/goods_specified_category.e2e.ts | 10 +++------- test/api/per_page.e2e.ts | 9 +++------ test/api/regexps.e2e.ts | 7 ++----- test/api/token.e2e.ts | 7 ++----- test/api/token_login_logout.e2e.ts | 7 ++----- test/api/user_usergroup.e2e.ts | 6 ++---- test/api/usergroups.e2e.ts | 8 ++------ test/api/users_goods.e2e.ts | 6 ++---- test/helpers/database.ts | 14 ++++---------- test/helpers/utils.ts | 15 +++++++++++++++ test/issues/ban_user_n_its_token.e2e.ts | 7 ++----- test/issues/github/github_issue_16.e2e.ts | 7 ++----- test/issues/github/github_issue_21.e2e.ts | 6 ++---- test/issues/github/github_issue_22.e2e.ts | 6 ++---- test/issues/github/github_issue_27.e2e.ts | 8 ++------ test/issues/github/github_issue_28.e2e.ts | 7 ++----- test/issues/github/github_issue_31.e2e.ts | 7 ++----- test/issues/github/github_issue_35.e2e.ts | 6 ++---- test/issues/gitlab/issue_1.e2e.ts | 9 ++------- test/issues/gitlab/issue_2.e2e.ts | 8 ++------ test/models/Categroies.spec.ts | 7 +++---- test/models/regexp.spec.ts | 8 +++----- test/models/user.spec.ts | 6 +++--- test/services/users.spec.ts | 7 +++---- 30 files changed, 82 insertions(+), 161 deletions(-) diff --git a/test/api/categories/categories.e2e.ts b/test/api/categories/categories.e2e.ts index 467a5fd..a3500fd 100644 --- a/test/api/categories/categories.e2e.ts +++ b/test/api/categories/categories.e2e.ts @@ -1,3 +1,4 @@ +import { newIds } from "../../helpers/utils"; import supertest = require("supertest"); import { connect, drop, newUser } from "../../helpers/database"; @@ -13,11 +14,7 @@ describe("Categories E2E Api", () => { return connect(); }); - const ids = { - users: [ ], - categories: [ ], - values: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/collections/collections.e2e.ts b/test/api/collections/collections.e2e.ts index 4b4602c..43dd9ac 100644 --- a/test/api/collections/collections.e2e.ts +++ b/test/api/collections/collections.e2e.ts @@ -10,7 +10,7 @@ import { } from "../../helpers/database"; import { init } from "../../helpers/server"; import { uploadFiles } from "../../helpers/files"; -import { sleep, newName } from "../../helpers/utils"; +import { sleep, newName, newIds } from "../../helpers/utils"; import auth = require("@db/auth"); import goodsDb = require("@db/goods"); import files = require("../../helpers/files"); @@ -23,13 +23,7 @@ describe("Collections E2E Api", () => { return connect(); }); - const ids = { - users: [ ], - categories: [ ], - regexps: [ ], - collections: [ ], - goods: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/collections/collections_token.e2e.ts b/test/api/collections/collections_token.e2e.ts index b0faa93..e48131c 100644 --- a/test/api/collections/collections_token.e2e.ts +++ b/test/api/collections/collections_token.e2e.ts @@ -11,6 +11,7 @@ import { } from "../../helpers/database"; import { init } from "../../helpers/server"; import { uploadFiles, newFile } from "../../helpers/files"; +import { newIds } from "../../helpers/utils"; describe("Token to Upload Files Api", () => { @@ -20,13 +21,7 @@ describe("Token to Upload Files Api", () => { return connect(); }); - const ids = { - users: [ ], - tokens: [ ], - collections: [ ], - categories: [ ], - regexps: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/files.e2e.ts b/test/api/files.e2e.ts index 5584563..bee64e8 100644 --- a/test/api/files.e2e.ts +++ b/test/api/files.e2e.ts @@ -4,7 +4,7 @@ import { HttpStatus } from "@nestjs/common"; import { connect, drop, addCategoryAndRegexp, newUser } from "../helpers/database"; import { uploadFile } from "../helpers/files"; -import { sleep } from "../helpers/utils"; +import { sleep, newIds } from "../helpers/utils"; import { init, initWithAuth } from "../helpers/server"; import auth = require("@db/auth"); @@ -16,12 +16,7 @@ describe("Files E2E Api", () => { connect(); }); - const ids = { - users: [ ], - regexps: [ ], - categories: [ ], - goods: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/goods/goods.e2e.ts b/test/api/goods/goods.e2e.ts index 67673d1..c6a460d 100644 --- a/test/api/goods/goods.e2e.ts +++ b/test/api/goods/goods.e2e.ts @@ -5,7 +5,7 @@ import { connect, drop, addCategoryAndRegexp } from "../../helpers/database"; import { uploadFile } from "../../helpers/files"; import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; describe("Goods E2E Api", () => { @@ -19,13 +19,7 @@ describe("Goods E2E Api", () => { connect(); }); - const ids = { - users: [ ], - categories: [ ], - values: [ ], - regexps: [ ], - goods: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/goods/goods_append.e2e.ts b/test/api/goods/goods_append.e2e.ts index 03e9086..5ce35a9 100644 --- a/test/api/goods/goods_append.e2e.ts +++ b/test/api/goods/goods_append.e2e.ts @@ -7,7 +7,7 @@ import goods = require("@db/goods"); import regexps = require("@db/regexps"); import files = require("../../helpers/files"); import categories = require("@db/categories"); -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; describe("Upload Good with Append categories", () => { @@ -21,12 +21,8 @@ describe("Upload Good with Append categories", () => { return db.connect(); }); - const ids = { - categories: [ ], - regexps: [ ], - users: [ ], - goods: [ ] - }; + const ids = newIds(); + after(() => { return db.drop(ids); }); diff --git a/test/api/goods/goods_specified_category.e2e.ts b/test/api/goods/goods_specified_category.e2e.ts index ff5b253..95cd205 100644 --- a/test/api/goods/goods_specified_category.e2e.ts +++ b/test/api/goods/goods_specified_category.e2e.ts @@ -7,7 +7,7 @@ import categories = require("@db/categories"); import * as regexps from "@db/regexps"; import * as goods from "@db/goods"; import { init } from "../../helpers/server"; -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; describe("Upload Good with specified categories", () => { @@ -21,12 +21,8 @@ describe("Upload Good with specified categories", () => { return db.connect(); }); - const ids = { - categories: [ ], - regexps: [ ], - users: [ ], - goods: [ ] - }; + const ids = newIds(); + after(() => { return db.drop(ids); }); diff --git a/test/api/per_page.e2e.ts b/test/api/per_page.e2e.ts index c364546..827db8c 100644 --- a/test/api/per_page.e2e.ts +++ b/test/api/per_page.e2e.ts @@ -4,7 +4,7 @@ import { } from "../helpers/database"; import { init } from "../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("the E2E Api of display item count Per page", () => { @@ -14,11 +14,8 @@ describe("the E2E Api of display item count Per page", () => { return connect(); }); - const ids = { - users: [ ], - regexps: [ ], - categories: [ ] - }; + const ids = newIds(); + after(() => { return drop(ids); }); diff --git a/test/api/regexps.e2e.ts b/test/api/regexps.e2e.ts index a7cc000..495d4de 100644 --- a/test/api/regexps.e2e.ts +++ b/test/api/regexps.e2e.ts @@ -4,7 +4,7 @@ import { Model as RegexpsModel } from "@models/Regexp"; import { RegexpsService } from "@services/regexps"; import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; -import { sleep } from "../helpers/utils"; +import { sleep, newIds } from "../helpers/utils"; import { newName } from "./../helpers/utils"; import auth = require("../helpers/database/auth"); @@ -18,10 +18,7 @@ describe("Regexp E2E Api", () => { return connect(); }); - const ids = { - users: [ ], - regexps: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/token.e2e.ts b/test/api/token.e2e.ts index 3944174..20045ad 100644 --- a/test/api/token.e2e.ts +++ b/test/api/token.e2e.ts @@ -3,7 +3,7 @@ import { TokensService } from "@services/tokens"; import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("Token E2E Test", () => { @@ -14,10 +14,7 @@ describe("Token E2E Test", () => { return connect(); }); - const ids = { - users: [ ], - tokens: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/token_login_logout.e2e.ts b/test/api/token_login_logout.e2e.ts index 9b32b14..2e2f186 100644 --- a/test/api/token_login_logout.e2e.ts +++ b/test/api/token_login_logout.e2e.ts @@ -3,7 +3,7 @@ import supertest = require("supertest"); import { connect, drop, newUser } from "../helpers/database"; import { init } from "../helpers/server"; import { Model as TokensModel } from "@models/Token"; -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("Token E2E Api", () => { @@ -13,10 +13,7 @@ describe("Token E2E Api", () => { return connect(); }); - const ids = { - users: [ ], - tokens: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/user_usergroup.e2e.ts b/test/api/user_usergroup.e2e.ts index df357d7..be94308 100644 --- a/test/api/user_usergroup.e2e.ts +++ b/test/api/user_usergroup.e2e.ts @@ -5,6 +5,7 @@ import { init } from "../helpers/server"; import { newUsergroup } from "@db/usergroups"; import { newUser, newUserWithUsergroup } from "@db/user"; import auth = require("@db/auth"); +import { newIds } from "../helpers/utils"; describe("User's Usergroup E2E Api", () => { @@ -14,10 +15,7 @@ describe("User's Usergroup E2E Api", () => { return connect(); }); - const ids = { - users: [ ], - usergroups: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/usergroups.e2e.ts b/test/api/usergroups.e2e.ts index 95a9225..70cf9f4 100644 --- a/test/api/usergroups.e2e.ts +++ b/test/api/usergroups.e2e.ts @@ -6,7 +6,7 @@ import { import { init } from "../helpers/server"; import { newUsergroup } from "@db/usergroups"; import auth = require("@db/auth"); -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("Usergroup E2E Api", () => { @@ -16,11 +16,7 @@ describe("Usergroup E2E Api", () => { return connect(); }); - const ids = { - users: [ ], - usergroups: [ ], - userusergroups: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/api/users_goods.e2e.ts b/test/api/users_goods.e2e.ts index fc64a60..c8e0494 100644 --- a/test/api/users_goods.e2e.ts +++ b/test/api/users_goods.e2e.ts @@ -5,7 +5,7 @@ import { } from "../helpers/database"; import { init } from "../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("User's Goods E2E Api", () => { @@ -15,9 +15,7 @@ describe("User's Goods E2E Api", () => { return connect(); }); - const ids = { - users: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/helpers/database.ts b/test/helpers/database.ts index 1137463..9a86e58 100644 --- a/test/helpers/database.ts +++ b/test/helpers/database.ts @@ -20,14 +20,16 @@ import { remove } from "./files"; config.db.database = "storebox-test"; -interface IIds { +export interface IIds { values?: ObjectId[]; goods?: ObjectId[]; regexps?: ObjectId[]; users?: ObjectId[]; - categories?: ObjectId[]; tokens?: ObjectId[]; + categories?: ObjectId[]; collections?: ObjectId[]; + usergroups?: ObjectId[]; + userusergroups?: ObjectId[]; } /** @@ -37,14 +39,6 @@ export const connect = connectDatabase; export const drop = async (ids?: IIds) => { await sleep(250); - if (!ids) { - await ValuesModel.remove({ }).exec(); - await GoodsModels.remove({ }).exec(); - await RegexpsModel.remove({ }).exec(); - await UsersModel.remove({ }).exec(); - await CategoriesModel.remove({ }).exec(); - return; - } const MODEL_IDMETHOD_MAP = { "values": ValuesModel, "goods": GoodsModels, diff --git a/test/helpers/utils.ts b/test/helpers/utils.ts index 3871ddd..ec7ad25 100644 --- a/test/helpers/utils.ts +++ b/test/helpers/utils.ts @@ -1,4 +1,5 @@ import faker = require("faker"); +import { IIds } from "./database"; export const sleep = (ms: number) => { return new Promise((reslove) => { @@ -11,3 +12,17 @@ export const newName = (prefix = "") => { const name = `${faker.random.word()}${randomStr}${Date.now()}`; return `${prefix}${name}`; }; + +export const newIds = (): IIds => { + return { + values: [ ], + goods: [ ], + regexps: [ ], + users: [ ], + tokens: [ ], + categories: [ ], + collections: [ ], + usergroups: [ ], + userusergroups: [ ] + }; +}; diff --git a/test/issues/ban_user_n_its_token.e2e.ts b/test/issues/ban_user_n_its_token.e2e.ts index 50c7f3d..9fa20d0 100644 --- a/test/issues/ban_user_n_its_token.e2e.ts +++ b/test/issues/ban_user_n_its_token.e2e.ts @@ -5,7 +5,7 @@ import { init } from "../helpers/server"; import { UsersService } from "@services/users"; import { TokensService } from "@services/tokens"; import { SystemService } from "@services/system"; -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("Fix Issues", () => { @@ -17,10 +17,7 @@ describe("Fix Issues", () => { return connect(); }); - const ids = { - users: [ ], - tokens: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/issues/github/github_issue_16.e2e.ts b/test/issues/github/github_issue_16.e2e.ts index 5035f43..b546b20 100644 --- a/test/issues/github/github_issue_16.e2e.ts +++ b/test/issues/github/github_issue_16.e2e.ts @@ -5,6 +5,7 @@ import { } from "../../helpers/database"; import { init } from "../../helpers/server"; import auth = require("@db/auth"); +import { newIds } from "../../helpers/utils"; /** * Fix [Issue 16](https://github.com/BoxSystem/StoreBox-Api/issues/16) @@ -17,11 +18,7 @@ describe("Fix Issues", () => { return connect(); }); - const ids = { - users: [ ], - categories: [ ], - regexps: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/issues/github/github_issue_21.e2e.ts b/test/issues/github/github_issue_21.e2e.ts index 50280db..f763886 100644 --- a/test/issues/github/github_issue_21.e2e.ts +++ b/test/issues/github/github_issue_21.e2e.ts @@ -5,7 +5,7 @@ import { } from "../../helpers/database"; import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; /** * About [Issue 21](https://github.com/BoxSystem/StoreBox-Api/issues/21) @@ -18,9 +18,7 @@ describe("Fix Issues", () => { return connect(); }); - const ids = { - users: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/issues/github/github_issue_22.e2e.ts b/test/issues/github/github_issue_22.e2e.ts index c0997f0..06301d0 100644 --- a/test/issues/github/github_issue_22.e2e.ts +++ b/test/issues/github/github_issue_22.e2e.ts @@ -5,7 +5,7 @@ import { } from "../../helpers/database"; import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; /** * Fix [Issue 22](https://github.com/BoxSystem/StoreBox-Api/issues/22) @@ -18,9 +18,7 @@ describe("Fix Issues", () => { return connect(); }); - const ids = { - users: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/issues/github/github_issue_27.e2e.ts b/test/issues/github/github_issue_27.e2e.ts index 232ee18..b9e6a47 100644 --- a/test/issues/github/github_issue_27.e2e.ts +++ b/test/issues/github/github_issue_27.e2e.ts @@ -5,7 +5,7 @@ import { } from "../../helpers/database"; import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; /** * Fix [Issue 27](https://github.com/BoxSystem/StoreBox-Api/issues/27) @@ -18,11 +18,7 @@ describe("Fix Issues", () => { return connect(); }); - const ids = { - users: [ ], - categories: [ ], - regexps: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/issues/github/github_issue_28.e2e.ts b/test/issues/github/github_issue_28.e2e.ts index 05158f2..4d49a27 100644 --- a/test/issues/github/github_issue_28.e2e.ts +++ b/test/issues/github/github_issue_28.e2e.ts @@ -5,6 +5,7 @@ import { } from "../../helpers/database"; import { init } from "../../helpers/server"; import auth = require("@db/auth"); +import { newIds } from "../../helpers/utils"; /** * Fix [Issue 28](https://github.com/BoxSystem/StoreBox-Api/issues/28) @@ -17,11 +18,7 @@ describe("Fix Issues", () => { return connect(); }); - const ids = { - users: [ ], - categories: [ ], - regexps: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/issues/github/github_issue_31.e2e.ts b/test/issues/github/github_issue_31.e2e.ts index af6ca86..7eaf7d8 100644 --- a/test/issues/github/github_issue_31.e2e.ts +++ b/test/issues/github/github_issue_31.e2e.ts @@ -11,6 +11,7 @@ import * as files from "../../helpers/files"; import { config } from "@utils/config"; import auth = require("@db/auth"); +import { newIds } from "../../helpers/utils"; /** * Fix [Issue 31](https://github.com/BoxSystem/StoreBox-Api/issues/31) @@ -23,11 +24,7 @@ describe("Fix Issues", () => { return connect(); }); - const ids = { - users: [ ], - categories: [ ], - regexps: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/issues/github/github_issue_35.e2e.ts b/test/issues/github/github_issue_35.e2e.ts index ae61507..a7c2a51 100644 --- a/test/issues/github/github_issue_35.e2e.ts +++ b/test/issues/github/github_issue_35.e2e.ts @@ -5,7 +5,7 @@ import { } from "../../helpers/database"; import { init } from "../../helpers/server"; import auth = require("@db/auth"); -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; /** * The Feature of Edit User @@ -19,9 +19,7 @@ describe("Fix Issues", () => { return connect(); }); - const ids = { - users: [ ] - }; + const ids = newIds(); after(() => { return drop(ids); diff --git a/test/issues/gitlab/issue_1.e2e.ts b/test/issues/gitlab/issue_1.e2e.ts index 6aa38dd..3ab2b7f 100644 --- a/test/issues/gitlab/issue_1.e2e.ts +++ b/test/issues/gitlab/issue_1.e2e.ts @@ -3,7 +3,7 @@ import categories = require("@db/categories"); import auth = require("@db/auth"); import * as db from "../../helpers/database"; import * as server from "../../helpers/server"; -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; import * as files from "../../helpers/files"; import goods = require("@db/goods"); @@ -18,12 +18,7 @@ describe("Fix Issues", () => { return db.connect(); }); - const ids = { - users: [ ], - categories: [ ], - regexps: [ ], - goods: [ ] - }; + const ids = newIds(); after(() => { return db.drop(ids); diff --git a/test/issues/gitlab/issue_2.e2e.ts b/test/issues/gitlab/issue_2.e2e.ts index f4805b8..be4cbd2 100644 --- a/test/issues/gitlab/issue_2.e2e.ts +++ b/test/issues/gitlab/issue_2.e2e.ts @@ -3,7 +3,7 @@ import categories = require("@db/categories"); import auth = require("@db/auth"); import * as db from "../../helpers/database"; import * as server from "../../helpers/server"; -import { newName } from "../../helpers/utils"; +import { newName, newIds } from "../../helpers/utils"; /** * Fix [Issue 2](http://git.pbr.link/Arylo/StoreBox/issues/2) @@ -16,11 +16,7 @@ describe("Fix Issues", () => { return db.connect(); }); - const ids = { - users: [ ], - categories: [ ], - regexps: [ ] - }; + const ids = newIds(); after(() => { return db.drop(ids); diff --git a/test/models/Categroies.spec.ts b/test/models/Categroies.spec.ts index c9061ae..5723a96 100644 --- a/test/models/Categroies.spec.ts +++ b/test/models/Categroies.spec.ts @@ -2,7 +2,7 @@ import { Model as CategoriesModel } from "@models/Categroy"; import { Model as ValuesModel } from "@models/Value"; import db = require("../helpers/database"); import { addCategories } from "../helpers/database/categories"; -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("Category Model", () => { @@ -10,9 +10,8 @@ describe("Category Model", () => { return db.connect(); }); - const ids = { - categories: [ ] - }; + const ids = newIds(); + after(() => { return db.drop(ids); }); diff --git a/test/models/regexp.spec.ts b/test/models/regexp.spec.ts index 2da0ff4..3bce308 100644 --- a/test/models/regexp.spec.ts +++ b/test/models/regexp.spec.ts @@ -2,7 +2,7 @@ import * as db from "../helpers/database"; import * as md5 from "md5"; import { Model as RegexpsModel, RegexpDoc } from "@models/Regexp"; import { Model as CategoryModel, ICategoryRaw } from "@models/Categroy"; -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("RegExp Model", () => { @@ -20,10 +20,8 @@ describe("RegExp Model", () => { Category = result.toObject() as ICategoryRaw; }); - const ids = { - categories: [ ], - regexps: [ ] - }; + const ids = newIds(); + afterEach(() => { return db.drop(ids); }); diff --git a/test/models/user.spec.ts b/test/models/user.spec.ts index c5ede62..d750d6c 100644 --- a/test/models/user.spec.ts +++ b/test/models/user.spec.ts @@ -2,6 +2,7 @@ import * as db from "../helpers/database"; import * as md5 from "md5"; import { Model as UsersModel } from "@models/User"; import { Observer, Observable, Subject } from "rxjs"; +import { newIds } from "../helpers/utils"; describe("User Model", () => { @@ -15,9 +16,8 @@ describe("User Model", () => { return db.connect(); }); - const ids = { - users: [ ] - }; + const ids = newIds(); + after(() => { return db.drop(ids); }); diff --git a/test/services/users.spec.ts b/test/services/users.spec.ts index f6186a9..f759c36 100644 --- a/test/services/users.spec.ts +++ b/test/services/users.spec.ts @@ -3,7 +3,7 @@ import { SystemService } from "@services/system"; import { Model as UsersModel } from "@models/User"; import db = require("../helpers/database"); import { newUser } from "../helpers/database"; -import { newName } from "../helpers/utils"; +import { newName, newIds } from "../helpers/utils"; describe("Users Service Test Unit", () => { @@ -13,9 +13,8 @@ describe("Users Service Test Unit", () => { return db.connect(); }); - const ids = { - users: [ ] - }; + const ids = newIds(); + after(() => { return db.drop(ids); }); From 7d700b232b6e3931656feab60e1c9eb49161852e Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Mar 2018 05:46:10 +0800 Subject: [PATCH 46/49] Move Service Files path --- src/{modules/common => }/services/base.service.ts | 0 src/{modules/common => }/services/categories.service.ts | 4 +++- src/{modules/common => }/services/collections.service.ts | 2 +- src/{modules/common => }/services/goods.service.ts | 0 src/{modules/common => }/services/regexps.service.ts | 0 src/{modules/common => }/services/system.service.ts | 4 ++-- src/{modules/common => }/services/tokens.service.ts | 0 src/{modules/common => }/services/usergroups.service.ts | 0 src/{modules/common => }/services/users.service.ts | 0 src/{modules/common => }/services/util.service.ts | 0 test/tsconfig.json | 2 +- tsconfig.json | 4 ++-- 12 files changed, 9 insertions(+), 7 deletions(-) rename src/{modules/common => }/services/base.service.ts (100%) rename src/{modules/common => }/services/categories.service.ts (97%) rename src/{modules/common => }/services/collections.service.ts (96%) rename src/{modules/common => }/services/goods.service.ts (100%) rename src/{modules/common => }/services/regexps.service.ts (100%) rename src/{modules/common => }/services/system.service.ts (97%) rename src/{modules/common => }/services/tokens.service.ts (100%) rename src/{modules/common => }/services/usergroups.service.ts (100%) rename src/{modules/common => }/services/users.service.ts (100%) rename src/{modules/common => }/services/util.service.ts (100%) diff --git a/src/modules/common/services/base.service.ts b/src/services/base.service.ts similarity index 100% rename from src/modules/common/services/base.service.ts rename to src/services/base.service.ts diff --git a/src/modules/common/services/categories.service.ts b/src/services/categories.service.ts similarity index 97% rename from src/modules/common/services/categories.service.ts rename to src/services/categories.service.ts index edde4fb..32f285a 100644 --- a/src/modules/common/services/categories.service.ts +++ b/src/services/categories.service.ts @@ -1,6 +1,8 @@ import { Component, BadRequestException } from "@nestjs/common"; import { ObjectId } from "@models/common"; -import { Model as CategoriesModel, cache, ICategoryRaw, ICategory } from "@models/Categroy"; +import { + Model as CategoriesModel, cache, ICategoryRaw, ICategory +} from "@models/Categroy"; import { isFunction, isArray } from "util"; import { BaseService, IGetOptions } from "@services/base"; import { difference } from "lodash"; diff --git a/src/modules/common/services/collections.service.ts b/src/services/collections.service.ts similarity index 96% rename from src/modules/common/services/collections.service.ts rename to src/services/collections.service.ts index b331795..35b93d4 100644 --- a/src/modules/common/services/collections.service.ts +++ b/src/services/collections.service.ts @@ -5,7 +5,7 @@ import { } from "@models/Collection"; import { ObjectId } from "@models/common"; import { DEF_PER_COUNT } from "@dtos/page"; -import { IEditCollection } from "../../../modules/collections/collections.dto"; +import { IEditCollection } from "../modules/collections/collections.dto"; import { BaseService, IGetOptions } from "@services/base"; @Component() diff --git a/src/modules/common/services/goods.service.ts b/src/services/goods.service.ts similarity index 100% rename from src/modules/common/services/goods.service.ts rename to src/services/goods.service.ts diff --git a/src/modules/common/services/regexps.service.ts b/src/services/regexps.service.ts similarity index 100% rename from src/modules/common/services/regexps.service.ts rename to src/services/regexps.service.ts diff --git a/src/modules/common/services/system.service.ts b/src/services/system.service.ts similarity index 97% rename from src/modules/common/services/system.service.ts rename to src/services/system.service.ts index 10a50ec..ebf3b0c 100644 --- a/src/modules/common/services/system.service.ts +++ b/src/services/system.service.ts @@ -6,7 +6,7 @@ import { BaseService, IGetOptions } from "@services/base"; import { isURL } from "validator"; import * as typescript from "typescript"; -import { systemLogger } from "../helper/log"; +import { systemLogger } from "../modules/common/helper/log"; export enum DEFAULTS { USERGROUP_FLAG = "DEFAULT_USERGROUP", @@ -172,7 +172,7 @@ export class SystemService extends BaseService { return { version: { typescript: typescript.version, - api: require("../../../../package.json").version, + api: require("../../package.json").version, node: process.versions }, env: { diff --git a/src/modules/common/services/tokens.service.ts b/src/services/tokens.service.ts similarity index 100% rename from src/modules/common/services/tokens.service.ts rename to src/services/tokens.service.ts diff --git a/src/modules/common/services/usergroups.service.ts b/src/services/usergroups.service.ts similarity index 100% rename from src/modules/common/services/usergroups.service.ts rename to src/services/usergroups.service.ts diff --git a/src/modules/common/services/users.service.ts b/src/services/users.service.ts similarity index 100% rename from src/modules/common/services/users.service.ts rename to src/services/users.service.ts diff --git a/src/modules/common/services/util.service.ts b/src/services/util.service.ts similarity index 100% rename from src/modules/common/services/util.service.ts rename to src/services/util.service.ts diff --git a/test/tsconfig.json b/test/tsconfig.json index a52a71c..1fa5afd 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -25,7 +25,7 @@ "@pipes/*": [ "./src/modules/common/pipes/*.pipe" ], "@guards/*": [ "./src/modules/common/guards/*.guard" ], "@dtos/*": [ "./src/modules/common/dtos/*.dto" ], - "@services/*": [ "./src/modules/common/services/*.service" ], + "@services/*": [ "./src/services/*.service" ], "@interceptors/*": [ "./src/modules/common/interceptors/*.interceptor" ], "@db/*": [ "./test/helpers/database/*" ] } diff --git a/tsconfig.json b/tsconfig.json index a70a974..a56a1d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -39,8 +39,8 @@ "./dist/modules/common/interceptors/*.interceptor" ], "@services/*": [ - "./src/modules/common/services/*.service", - "./dist/modules/common/services/*.service" + "./src/services/*.service", + "./dist/services/*.service" ] } }, From ec345866ed035612d1ced943f475045341d5ab3b Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Mar 2018 06:07:18 +0800 Subject: [PATCH 47/49] Move helpers to utils and add new cache factory --- src/express.ts | 2 +- src/index.ts | 4 ++-- src/models/Categroy.ts | 11 +++-------- src/models/Collection.ts | 12 +++--------- src/models/Good.ts | 11 +++-------- src/models/Regexp.ts | 13 ++++--------- src/models/System.ts | 14 ++++---------- src/models/Token.ts | 14 ++++---------- src/models/User.ts | 11 +++-------- src/models/Usergroup.ts | 3 +++ .../common/middlewares/logger.middleware.ts | 2 +- src/modules/database/database.providers.ts | 2 +- src/services/system.service.ts | 2 +- src/services/usergroups.service.ts | 5 ++++- src/utils/config.ts | 2 +- src/{modules/common/helper => utils}/env.ts | 0 src/{modules/common/helper => utils}/log.ts | 0 src/utils/newCache.ts | 10 ++++++++++ 18 files changed, 48 insertions(+), 70 deletions(-) rename src/{modules/common/helper => utils}/env.ts (100%) rename src/{modules/common/helper => utils}/log.ts (100%) create mode 100644 src/utils/newCache.ts diff --git a/src/express.ts b/src/express.ts index 27e29cb..3fd5e1d 100644 --- a/src/express.ts +++ b/src/express.ts @@ -7,7 +7,7 @@ import { config } from "@utils/config"; import helmet = require("helmet"); import { error } from "./modules/common/middlewares/logger.middleware"; -import { isTest } from "./modules/common/helper/env"; +import { isTest } from "@utils/env"; const RedisStore = connectRedis(session); diff --git a/src/index.ts b/src/index.ts index 4c9d6c0..1c16643 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,8 +4,8 @@ import { config } from "@utils/config"; import { initExpress } from "./express"; import { ApplicationModule } from "./modules/app.module"; import { ValidationPipe } from "@pipes/validation"; -import { systemLogger } from "./modules/common/helper/log"; -import { isDevelopment } from "./modules/common/helper/env"; +import { systemLogger } from "@utils/log"; +import { isDevelopment } from "@utils/env"; const bootstrap = async () => { const server = initExpress(); diff --git a/src/models/Categroy.ts b/src/models/Categroy.ts index 8bc8fda..7ff92f0 100644 --- a/src/models/Categroy.ts +++ b/src/models/Categroy.ts @@ -5,17 +5,12 @@ import { DEF_PER_COUNT } from "@dtos/page"; import { isArray } from "util"; import { reduce, includes, difference } from "lodash"; import { MongoError } from "mongodb"; -import { config } from "@utils/config"; -import keyv = require("keyv"); +import newCache = require("@utils/newCache"); -import { isTest } from "../modules/common/helper/env"; +export const FLAG = "categories"; -export const cache = new keyv({ - uri: isTest ? undefined : config.redis.url, - namespace: "Categories" -}); +export const cache = newCache(FLAG); -export const FLAG = "categories"; export type CategoryDoc = IDoc; const Definition: SchemaDefinition = { diff --git a/src/models/Collection.ts b/src/models/Collection.ts index c68ceb6..815aeac 100644 --- a/src/models/Collection.ts +++ b/src/models/Collection.ts @@ -4,18 +4,12 @@ import { } from "@models/common"; import { IGoods, FLAG as GoodFlag, Model as GoodsModels } from "@models/Good"; import { IUser, FLAG as UserFlag } from "@models/User"; -import { config } from "@utils/config"; -import keyv = require("keyv"); - -import { isTest } from "../modules/common/helper/env"; - -export const cache = new keyv({ - uri: isTest ? undefined : config.redis.url, - namespace: "Collections" -}); +import newCache = require("@utils/newCache"); export const FLAG = "collections"; +export const cache = newCache(FLAG); + const Definition: SchemaDefinition = { name: { type: String, diff --git a/src/models/Good.ts b/src/models/Good.ts index b0604ac..acabad6 100644 --- a/src/models/Good.ts +++ b/src/models/Good.ts @@ -5,17 +5,12 @@ import { import { IValues, Flag as ValueFlag } from "@models/Value"; import { IUser, FLAG as UserFlag } from "@models/User"; import { ICategory, FLAG as CategoryFlag } from "@models/Categroy"; -import { config } from "@utils/config"; -import keyv = require("keyv"); +import newCache = require("@utils/newCache"); -import { isTest } from "../modules/common/helper/env"; +export const FLAG = "goods"; -export const cache = new keyv({ - uri: isTest ? undefined : config.redis.url, - namespace: "Goods" -}); +export const cache = newCache(FLAG); -export const FLAG = "goods"; export type GoodDoc = IDoc; const Definition: SchemaDefinition = { diff --git a/src/models/Regexp.ts b/src/models/Regexp.ts index 2e1494b..5603d41 100644 --- a/src/models/Regexp.ts +++ b/src/models/Regexp.ts @@ -5,16 +5,13 @@ import { import { ICategory, FLAG as CF, Model as CM } from "@models/Categroy"; import { DEF_PER_COUNT } from "@dtos/page"; import isRegExp = require("@utils/isRegExp"); -import { config } from "@utils/config"; -import keyv = require("keyv"); +import newCache = require("@utils/newCache"); import { INewRegexp } from "../modules/regexps/regexps.dto"; -import { isTest } from "../modules/common/helper/env"; -export const cache = new keyv({ - uri: isTest ? undefined : config.redis.url, - namespace: "Regexps" -}); +export const FLAG = "regexps"; + +export const cache = newCache(FLAG); const Definition: SchemaDefinition = { name: { type: String, required: true, unique: true }, @@ -69,8 +66,6 @@ RegexpSchema.static("removeRegexp", (id: ObjectId) => { // endregion static methods -export const FLAG = "regexps"; - interface IRegexpModel extends M { /** * 创建新规则 diff --git a/src/models/System.ts b/src/models/System.ts index 80eb992..472f53d 100644 --- a/src/models/System.ts +++ b/src/models/System.ts @@ -1,14 +1,10 @@ import { model, SchemaDefinition, Model as M } from "mongoose"; import { Base, IDoc, IDocRaw, MODIFY_MOTHODS } from "./common"; -import { config } from "@utils/config"; -import keyv = require("keyv"); +import newCache = require("@utils/newCache"); -import { isTest } from "../modules/common/helper/env"; +export const FLAG = "system"; -export const cache = new keyv({ - uri: isTest ? undefined : config.redis.url, - namespace: "System" -}); +export const cache = newCache(FLAG); const Definition: SchemaDefinition = { key: { type: String, required: true }, @@ -22,8 +18,6 @@ export interface ISystem extends IDocRaw { const SystemSchema = new Base(Definition).createSchema(); -export const Flag = "system"; - export type SystemDoc = IDoc; for (const method of MODIFY_MOTHODS) { @@ -32,4 +26,4 @@ for (const method of MODIFY_MOTHODS) { }); } -export const Model: M = model(Flag, SystemSchema); +export const Model: M = model(FLAG, SystemSchema); diff --git a/src/models/Token.ts b/src/models/Token.ts index 3eff4a7..0c623ed 100644 --- a/src/models/Token.ts +++ b/src/models/Token.ts @@ -3,17 +3,11 @@ import { Base, IDoc, IDocRaw, ObjectId, MODIFY_MOTHODS, existsValidator } from "@models/common"; import { IUser, FLAG as UserFlag } from "@models/User"; -import { config } from "@utils/config"; -import keyv = require("keyv"); +import newCache = require("@utils/newCache"); -import { isTest } from "../modules/common/helper/env"; +export const FLAG = "tokens"; -export const cache = new keyv({ - uri: isTest ? undefined : config.redis.url, - namespace: "Tokens" -}); - -export const Flag = "tokens"; +export const cache = newCache(FLAG); const Definition: SchemaDefinition = { token: { type: String, unique: true, index: true }, @@ -64,7 +58,7 @@ for (const method of MODIFY_MOTHODS) { }); } -export const Model = model(Flag, TokensSchema) as M; +export const Model = model(FLAG, TokensSchema) as M; const getCount = (userId: ObjectId): Promise => { return Model.count({ user: userId }).exec(); diff --git a/src/models/User.ts b/src/models/User.ts index 6b10a86..6cf739f 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -4,17 +4,12 @@ import { config } from "@utils/config"; import { ObjectId, existsValidator } from "@models/common"; import { DEF_PER_COUNT } from "@dtos/page"; import { Base, IDoc, IDocRaw, MODIFY_MOTHODS } from "./common"; -import keyv = require("keyv"); - -import { isTest } from "../modules/common/helper/env"; - -export const cache = new keyv({ - uri: isTest ? undefined : config.redis.url, - namespace: "Users" -}); +import newCache = require("@utils/newCache"); export const FLAG = "users"; +export const cache = newCache(FLAG); + const Definition: SchemaDefinition = { username: { type: String, required: true, trim: true }, password: { type: String, required: true }, diff --git a/src/models/Usergroup.ts b/src/models/Usergroup.ts index d5880eb..2a9bcef 100644 --- a/src/models/Usergroup.ts +++ b/src/models/Usergroup.ts @@ -2,6 +2,7 @@ import { model, SchemaDefinition, Model as M, SchemaTypes } from "mongoose"; import { FLAG as UF, IUser } from "@models/User"; import { ObjectId, existsValidator } from "@models/common"; import { Base, IDoc, IDocRaw } from "./common"; +import newCache = require("@utils/newCache"); const Definition: SchemaDefinition = { name: { type: String, required: true } @@ -9,6 +10,8 @@ const Definition: SchemaDefinition = { export const FLAG = "usergroups"; +export const cache = newCache(FLAG); + export interface IUsergroups extends IDocRaw { name: string; } diff --git a/src/modules/common/middlewares/logger.middleware.ts b/src/modules/common/middlewares/logger.middleware.ts index 00da8fc..053a1b4 100644 --- a/src/modules/common/middlewares/logger.middleware.ts +++ b/src/modules/common/middlewares/logger.middleware.ts @@ -4,7 +4,7 @@ import { } from "@nestjs/common"; import { getMeta, downloadLogger, apiLogger, accessLogger, systemLogger -} from "../helper/log"; +} from "@utils/log"; const access: RequestHandler = (req, res, next) => { // const logger = accessLogger; diff --git a/src/modules/database/database.providers.ts b/src/modules/database/database.providers.ts index d03a91a..db4af71 100644 --- a/src/modules/database/database.providers.ts +++ b/src/modules/database/database.providers.ts @@ -5,7 +5,7 @@ import { Model as UsersModel } from "@models/User"; import { Model as UsergroupsModel } from "@models/Usergroup"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { SystemService } from "@services/system"; -import { systemLogger } from "../common/helper/log"; +import { systemLogger } from "@utils/log"; const getDatabaseUrl = () => { let host: string[] = [ ]; diff --git a/src/services/system.service.ts b/src/services/system.service.ts index ebf3b0c..eb18317 100644 --- a/src/services/system.service.ts +++ b/src/services/system.service.ts @@ -6,7 +6,7 @@ import { BaseService, IGetOptions } from "@services/base"; import { isURL } from "validator"; import * as typescript from "typescript"; -import { systemLogger } from "../modules/common/helper/log"; +import { systemLogger } from "@utils/log"; export enum DEFAULTS { USERGROUP_FLAG = "DEFAULT_USERGROUP", diff --git a/src/services/usergroups.service.ts b/src/services/usergroups.service.ts index a783908..184f0dd 100644 --- a/src/services/usergroups.service.ts +++ b/src/services/usergroups.service.ts @@ -1,6 +1,8 @@ import { Component, BadRequestException } from "@nestjs/common"; import { Model as UsersModel } from "@models/User"; -import { Model as UsergroupsModel, IUsergroups } from "@models/Usergroup"; +import { + Model as UsergroupsModel, IUsergroups, cache +} from "@models/Usergroup"; import { Model as UserUsergroupsModel } from "@models/User-Usergroup"; import { ObjectId } from "@models/common"; import { BaseService } from "@services/base"; @@ -10,6 +12,7 @@ export class UsergroupsService extends BaseService { constructor() { super(); + this.setCache(cache); this.setModel(UsergroupsModel); } diff --git a/src/utils/config.ts b/src/utils/config.ts index aea0453..7079b1d 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -57,5 +57,5 @@ try { export const config = configModule.getConfig() as ConfigObj; -import { systemLogger } from "../modules/common/helper/log"; +import { systemLogger } from "./log"; systemLogger.debug(config); diff --git a/src/modules/common/helper/env.ts b/src/utils/env.ts similarity index 100% rename from src/modules/common/helper/env.ts rename to src/utils/env.ts diff --git a/src/modules/common/helper/log.ts b/src/utils/log.ts similarity index 100% rename from src/modules/common/helper/log.ts rename to src/utils/log.ts diff --git a/src/utils/newCache.ts b/src/utils/newCache.ts new file mode 100644 index 0000000..ee585ad --- /dev/null +++ b/src/utils/newCache.ts @@ -0,0 +1,10 @@ +import keyv = require("keyv"); +import { isTest } from "@utils/env"; +import { config } from "@utils/config"; + +export = (namespace: string) => { + return new keyv({ + uri: isTest ? undefined : config.redis.url, + namespace + }); +}; From 2fcaf2325f9863298ed5d0b9b14dcb1721f09852 Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Mar 2018 06:20:39 +0800 Subject: [PATCH 48/49] Add istanbul ignore comment --- src/modules/files/files.controller.ts | 13 ++++------ src/services/base.service.ts | 37 +++++++++++++++------------ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/modules/files/files.controller.ts b/src/modules/files/files.controller.ts index dac526b..fce6fb6 100644 --- a/src/modules/files/files.controller.ts +++ b/src/modules/files/files.controller.ts @@ -38,14 +38,10 @@ export class FilesController { public async downloadFile( @Req() req, @Res() res: Response, @Param() params: DownlaodDto ) { - let obj: GoodDoc; - try { - obj = (await this.goodsSvr.get({ - _id: params.id, category: params.cid - }))[0]; - } catch (error) { - throw new BadRequestException(error.toString()); - } + const obj = (await this.goodsSvr.get({ + _id: params.id, category: params.cid + }))[0]; + if (!obj) { throw new NotFoundException(); } @@ -57,6 +53,7 @@ export class FilesController { throw new BadRequestException("Disallow download the File"); } res.download(filepath, good.originname, (err) => { + /* istanbul ignore if */ if (err) { // Recode Error } diff --git a/src/services/base.service.ts b/src/services/base.service.ts index 1bcb9fc..5f988ce 100644 --- a/src/services/base.service.ts +++ b/src/services/base.service.ts @@ -29,6 +29,7 @@ abstract class ModelService { } private checkModel() { + /* istanbul ignore if */ if (!this.model) { throw new BadGatewayException("Lost Model"); } @@ -52,33 +53,36 @@ abstract class ModelService { public async create(obj: object) { this.checkModel(); - await this.beforeAll(); - await this.beforeEach(); + await this.runBeforeAll(); + await this.runBeforeEach(); try { return await this.model.create(obj); } catch (error) { + /* istanbul ignore next */ throw new BadRequestException(error.toString()); } } public async delete(cond: object) { this.checkModel(); - await this.beforeAll(); - await this.beforeEach(); + await this.runBeforeAll(); + await this.runBeforeEach(); try { return await this.model.findOneAndRemove(cond).exec(); } catch (error) { + /* istanbul ignore next */ throw new BadRequestException(error.toString()); } } public async deleteById(id: ObjectId) { this.checkModel(); - await this.beforeAll(); - await this.beforeEach(); + await this.runBeforeAll(); + await this.runBeforeEach(); try { return await this.model.findByIdAndRemove(id).exec(); } catch (error) { + /* istanbul ignore next */ throw new BadRequestException(error.toString()); } } @@ -87,20 +91,21 @@ abstract class ModelService { id: ObjectId, ctx: object, opts = this.DEF_UPDATE_OPTIONS ) { this.checkModel(); - await this.beforeAll(); - await this.beforeEach(); + await this.runBeforeAll(); + await this.runBeforeEach(); const options = Object.assign({ }, this.DEF_UPDATE_OPTIONS, opts); try { return await this.model.update({ _id: id }, ctx, options).exec(); } catch (error) { + /* istanbul ignore next */ throw new BadRequestException(error.toString()); } } protected async find(cond: object, opts?: IGetOptions) { this.checkModel(); - await this.beforeAll(); - await this.beforeEach(); + await this.runBeforeAll(); + await this.runBeforeEach(); const p = this.model.find(cond); return this.documentQueryProcess(p, opts).exec(); } @@ -113,8 +118,8 @@ abstract class ModelService { protected async findOne(cond: object, opts?: IGetOptions) { this.checkModel(); - await this.beforeAll(); - await this.beforeEach(); + await this.runBeforeAll(); + await this.runBeforeEach(); const p = this.model.findOne(cond); return this.documentQueryProcess(p, opts).exec(); } @@ -127,8 +132,8 @@ abstract class ModelService { protected async findById(id: ObjectId, opts?: IGetOptions) { this.checkModel(); - await this.beforeAll(); - await this.beforeEach(); + await this.runBeforeAll(); + await this.runBeforeEach(); const p = this.model.findById(id); return this.documentQueryProcess(p, opts).exec(); } @@ -141,8 +146,8 @@ abstract class ModelService { protected async total(cond: object = { }) { this.checkModel(); - await this.beforeAll(); - await this.beforeEach(); + await this.runBeforeAll(); + await this.runBeforeEach(); return this.model.count(cond).exec(); } From feb1899b62ebbc3b3961a3ab85aff8de39e3497a Mon Sep 17 00:00:00 2001 From: Arylo Date: Mon, 26 Mar 2018 06:21:14 +0800 Subject: [PATCH 49/49] Step to v1.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fdf1aee..dfa916c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "storebox-api", - "version": "1.2.0", + "version": "1.3.0", "description": "", "main": "index.js", "scripts": {