diff --git a/server/.gitignore b/server/.gitignore index 286e579e..574e9bb2 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -35,4 +35,6 @@ lerna-debug.log* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json -!.vscode/extensions.json \ No newline at end of file +!.vscode/extensions.json + +tmp \ No newline at end of file diff --git a/server/package.json b/server/package.json index 85273956..8ab12443 100644 --- a/server/package.json +++ b/server/package.json @@ -51,8 +51,12 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", + "@types/ali-oss": "^6.16.11", "@types/express": "^4.17.17", + "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.2", + "@types/jsonwebtoken": "^9.0.6", + "@types/lodash": "^4.17.0", "@types/multer": "^1.4.11", "@types/node": "^20.3.1", "@types/node-forge": "^1.3.11", diff --git a/server/src/guards/authtication.spec.ts b/server/src/guards/authtication.spec.ts index 125589b9..35512ae4 100644 --- a/server/src/guards/authtication.spec.ts +++ b/server/src/guards/authtication.spec.ts @@ -1,16 +1,15 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Authtication } from './authtication'; -import { UserService } from '../modules/auth/services/user.service'; import { ConfigService } from '@nestjs/config'; -import { AuthenticationException } from '../exceptions/authException'; +import { Authtication } from './authtication'; +import { AuthService } from 'src/modules/auth/services/auth.service'; +import { AuthenticationException } from 'src/exceptions/authException'; import { User } from 'src/models/user.entity'; -import * as jwt from 'jsonwebtoken'; jest.mock('jsonwebtoken'); describe('Authtication', () => { let guard: Authtication; - let userService: UserService; + let authService: AuthService; let configService: ConfigService; beforeEach(async () => { @@ -18,9 +17,9 @@ describe('Authtication', () => { providers: [ Authtication, { - provide: UserService, + provide: AuthService, useValue: { - getUserByUsername: jest.fn(), + verifyToken: jest.fn(), }, }, { @@ -33,7 +32,7 @@ describe('Authtication', () => { }).compile(); guard = module.get(Authtication); - userService = module.get(UserService); + authService = module.get(AuthService); configService = module.get(ConfigService); }); @@ -62,7 +61,9 @@ describe('Authtication', () => { }), }; - jest.spyOn(jwt, 'verify').mockReturnValue(new Error('token is invalid')); + jest + .spyOn(authService, 'verifyToken') + .mockRejectedValue(new Error('token is invalid')); jest .spyOn(configService, 'get') @@ -73,31 +74,6 @@ describe('Authtication', () => { ); }); - it('should throw exception if user does not exist', async () => { - const context = { - switchToHttp: () => ({ - getRequest: () => ({ - headers: { - authorization: 'Bearer validToken', - }, - }), - }), - }; - - const fakeUser = { username: 'testUser' } as User; - - jest.spyOn(jwt, 'verify').mockReturnValue(fakeUser); - - jest - .spyOn(configService, 'get') - .mockReturnValue('XIAOJU_SURVEY_JWT_SECRET'); - jest.spyOn(userService, 'getUserByUsername').mockResolvedValue(null); - - await expect(guard.canActivate(context as any)).rejects.toThrow( - AuthenticationException, - ); - }); - it('should set user in request object and return true if user exists', async () => { const request = { headers: { @@ -114,9 +90,7 @@ describe('Authtication', () => { jest .spyOn(configService, 'get') .mockReturnValue('XIAOJU_SURVEY_JWT_SECRET'); - jest.spyOn(userService, 'getUserByUsername').mockResolvedValue(fakeUser); - - jest.spyOn(jwt, 'verify').mockReturnValue(fakeUser); + jest.spyOn(authService, 'verifyToken').mockResolvedValue(fakeUser); const result = await guard.canActivate(context as any); diff --git a/server/src/guards/authtication.ts b/server/src/guards/authtication.ts index 41c384a6..77664fc3 100644 --- a/server/src/guards/authtication.ts +++ b/server/src/guards/authtication.ts @@ -1,14 +1,10 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; -import { UserService } from '../modules/auth/services/user.service'; import { AuthenticationException } from '../exceptions/authException'; import { AuthService } from 'src/modules/auth/services/auth.service'; @Injectable() export class Authtication implements CanActivate { - constructor( - private readonly userService: UserService, - private readonly authService: AuthService, - ) {} + constructor(private readonly authService: AuthService) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); diff --git a/server/src/modules/auth/__test/auth.service.spec.ts b/server/src/modules/auth/__test/auth.service.spec.ts index 6bcc9d36..9d2639fd 100644 --- a/server/src/modules/auth/__test/auth.service.spec.ts +++ b/server/src/modules/auth/__test/auth.service.spec.ts @@ -1,18 +1,33 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { AuthService } from '../services/auth.service'; -import { sign } from 'jsonwebtoken'; +import { ConfigModule } from '@nestjs/config'; +import * as jwt from 'jsonwebtoken'; +import { User } from 'src/models/user.entity'; -jest.mock('jsonwebtoken'); +import { AuthService } from '../services/auth.service'; +import { UserService } from '../services/user.service'; + +jest.mock('jsonwebtoken', () => { + return { + sign: jest.fn(), + verify: jest.fn().mockReturnValue({}), + }; +}); describe('AuthService', () => { let service: AuthService; + let userService: UserService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [AuthService], + imports: [ConfigModule], + providers: [ + AuthService, + { provide: UserService, useValue: { getUserByUsername: jest.fn() } }, + ], }).compile(); service = module.get(AuthService); + userService = module.get(UserService); }); describe('generateToken', () => { @@ -25,9 +40,20 @@ describe('AuthService', () => { await service.generateToken(userData, tokenConfig); - expect(sign).toHaveBeenCalledWith(userData, tokenConfig.secret, { + expect(jwt.sign).toHaveBeenCalledWith(userData, tokenConfig.secret, { expiresIn: tokenConfig.expiresIn, }); }); }); + + describe('verifyToken', () => { + it('should verifyToken succeed', async () => { + const token = 'mock token'; + jest + .spyOn(userService, 'getUserByUsername') + .mockResolvedValue({} as User); + await service.verifyToken(token); + expect(userService.getUserByUsername).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/server/src/modules/file/__test/alioss.handler.spec.ts b/server/src/modules/file/__test/alioss.handler.spec.ts new file mode 100644 index 00000000..b570c521 --- /dev/null +++ b/server/src/modules/file/__test/alioss.handler.spec.ts @@ -0,0 +1,55 @@ +import { AliOssHandler } from '../services/uploadHandlers/alioss.handler'; + +describe('AliOssHandler', () => { + describe('upload', () => { + it('should upload a file and return the key', async () => { + const file = { + originalname: 'mockFileName.txt', + buffer: Buffer.from('mockFileContent'), + } as Express.Multer.File; + const mockPutResult = { name: 'mockFileName.txt' }; + const mockClient = { + put: jest.fn().mockResolvedValue(mockPutResult), + }; + const handler = new AliOssHandler({ + client: mockClient, + useSSL: true, + isPrivateRead: true, + expiryTime: '1h', + }); + + const result = await handler.upload(file); + + expect(result).toEqual({ + key: expect.stringMatching(/\w+\.txt/), + }); + }); + }); + + describe('getUrl', () => { + it('should return the URL for the given key', () => { + const key = 'mockFilePath/mockFileName.txt'; + const bucket = 'xiaojusurvey'; + const accessKey = 'mockAccessKey'; + const region = 'mockRegion'; + const handler = new AliOssHandler({ + accessKey, + secretKey: 'mockSecretKey', + bucket, + region, + endPoint: 'mockEndPoint', + useSSL: true, + isPrivateRead: true, + expiryTime: '1h', + }); + + const result = handler.getUrl(key); + const keyReg = new RegExp(key); + const signatureReg = new RegExp('Signature='); + const expiredReg = new RegExp('Expires='); + expect(keyReg.test(result)).toBe(true); + expect(signatureReg.test(result)).toBe(true); + expect(expiredReg.test(result)).toBe(true); + }); + }); +}); diff --git a/server/src/modules/file/__test/file.controller.spec.ts b/server/src/modules/file/__test/file.controller.spec.ts new file mode 100644 index 00000000..3a14dd5f --- /dev/null +++ b/server/src/modules/file/__test/file.controller.spec.ts @@ -0,0 +1,150 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigModule, ConfigService } from '@nestjs/config'; + +import { FileController } from '../controllers/file.controller'; +import { FileService } from '../services/file.service'; +import { uploadConfig, channels } from '../config/index'; + +import { AuthenticationException } from 'src/exceptions/authException'; +import { HttpException } from 'src/exceptions/httpException'; +import { AuthService } from 'src/modules/auth/services/auth.service'; +import { User } from 'src/models/user.entity'; + +describe('FileController', () => { + let controller: FileController; + let fileService: FileService; + let authService: AuthService; + // let configService: ConfigService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forFeature(() => { + return { + ...channels, + ...uploadConfig, + }; + }), + ], + controllers: [FileController], + providers: [ + FileService, + { + provide: AuthService, + useValue: { + verifyToken: jest.fn(), + }, + }, + ConfigService, + ], + }).compile(); + + controller = module.get(FileController); + fileService = module.get(FileService); + authService = module.get(AuthService); + // configService = module.get(ConfigService); + }); + + describe('upload', () => { + it('should upload a file', async () => { + const file: Express.Multer.File = { + fieldname: '', + originalname: '', + encoding: '', + mimetype: '', + size: 0, + stream: null, + destination: '', + filename: '', + path: '', + buffer: null, + }; + const req = { headers: { authorization: 'Bearer mockToken' } }; + const reqBody = { channel: 'upload' }; + + jest.spyOn(authService, 'verifyToken').mockResolvedValueOnce({} as User); + + const mockUploadResult = { key: 'mockKey', url: 'mockUrl' }; + jest.spyOn(fileService, 'upload').mockResolvedValueOnce(mockUploadResult); + + const result = await controller.upload(file, req, reqBody); + + expect(result).toEqual({ + code: 200, + data: { + url: mockUploadResult.url, + key: mockUploadResult.key, + }, + }); + }); + + it('should upload failed without token', async () => { + const file: Express.Multer.File = { + fieldname: '', + originalname: '', + encoding: '', + mimetype: '', + size: 0, + stream: null, + destination: '', + filename: '', + path: '', + buffer: null, + }; + const req = { headers: { authorization: '' } }; + const reqBody = { channel: 'upload' }; + + await expect(controller.upload(file, req, reqBody)).rejects.toThrow( + AuthenticationException, + ); + }); + + it('should upload failed', async () => { + const file: Express.Multer.File = { + fieldname: '', + originalname: '', + encoding: '', + mimetype: '', + size: 0, + stream: null, + destination: '', + filename: '', + path: '', + buffer: null, + }; + const req = { headers: { authorization: 'Bearer mockToken' } }; + const reqBody = { channel: 'mockChannel' }; + + await expect(controller.upload(file, req, reqBody)).rejects.toThrow( + HttpException, + ); + }); + }); + + describe('generateGetUrl', () => { + it('should generate a URL for the given channel and key', async () => { + const reqBody = { channel: 'upload', key: 'mockKey' }; + + const mockUrl = 'mockGeneratedUrl'; + jest.spyOn(fileService, 'getUrl').mockReturnValueOnce(mockUrl); + + const result = await controller.generateGetUrl(reqBody); + + expect(result).toEqual({ + code: 200, + data: { + key: reqBody.key, + url: mockUrl, + }, + }); + }); + + it('should generate a URL failed', async () => { + const reqBody = { channel: 'mockChannel', key: 'mockKey' }; + + await expect(controller.generateGetUrl(reqBody)).rejects.toThrow( + HttpException, + ); + }); + }); +}); diff --git a/server/src/modules/file/__test/file.service.spec.ts b/server/src/modules/file/__test/file.service.spec.ts new file mode 100644 index 00000000..1cafcd77 --- /dev/null +++ b/server/src/modules/file/__test/file.service.spec.ts @@ -0,0 +1,65 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigModule, ConfigService } from '@nestjs/config'; + +import { FileService } from '../services/file.service'; +import { LocalHandler } from '../services/uploadHandlers/local.handler'; +import { uploadConfig, channels } from '../config/index'; + +describe('FileService', () => { + let fileService: FileService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forFeature(() => { + return { + ...channels, + ...uploadConfig, + }; + }), + ], + providers: [FileService, ConfigService], + }).compile(); + + fileService = module.get(FileService); + }); + + describe('upload', () => { + it('should upload a file and return key and URL', async () => { + const configKey = 'mockConfigKey'; + const file = {} as Express.Multer.File; + const pathPrefix = 'mockPathPrefix'; + + const mockKey = 'mockKey'; + jest + .spyOn(LocalHandler.prototype, 'upload') + .mockResolvedValueOnce({ key: mockKey }); + + const mockUrl = 'mockUrl'; + jest + .spyOn(LocalHandler.prototype, 'getUrl') + .mockResolvedValueOnce(mockUrl as never); + + const result = await fileService.upload({ configKey, file, pathPrefix }); + + expect(result).toEqual({ + key: mockKey, + url: mockUrl, + }); + }); + }); + + describe('getUrl', () => { + it('should return URL for the given configKey and key', async () => { + const configKey = 'mockConfigKey'; + const key = 'mockKey'; + + const mockUrl = 'mockUrl'; + jest.spyOn(LocalHandler.prototype, 'getUrl').mockReturnValueOnce(mockUrl); + + const result = fileService.getUrl({ configKey, key }); + + expect(result).toEqual(mockUrl); + }); + }); +}); diff --git a/server/src/modules/file/__test/local.handler.spec.ts b/server/src/modules/file/__test/local.handler.spec.ts new file mode 100644 index 00000000..340e1277 --- /dev/null +++ b/server/src/modules/file/__test/local.handler.spec.ts @@ -0,0 +1,32 @@ +import { join } from 'path'; +import { LocalHandler } from '../services/uploadHandlers/local.handler'; + +describe('LocalHandler', () => { + describe('upload', () => { + it('should upload a file and return the key', async () => { + const file = { + originalname: `mockFileName.txt`, + buffer: Buffer.from('mockFileContent'), + } as Express.Multer.File; + const physicalRootPath = join(__dirname, 'tmp'); + const handler = new LocalHandler({ physicalRootPath }); + + const result = await handler.upload(file); + expect(result).toEqual({ + key: expect.stringMatching(/\w+\.txt/), + }); + }); + }); + + describe('getUrl', () => { + it('should return the URL for the given key', () => { + const key = 'mockFilePath/mockFileName.txt'; + const physicalRootPath = join(__dirname, 'tmp'); + const handler = new LocalHandler({ physicalRootPath }); + + const result = handler.getUrl(key); + + expect(result).toBe(`/${key}`); + }); + }); +}); diff --git a/server/src/modules/file/__test/minio.handler.spec.ts b/server/src/modules/file/__test/minio.handler.spec.ts new file mode 100644 index 00000000..66bcce9e --- /dev/null +++ b/server/src/modules/file/__test/minio.handler.spec.ts @@ -0,0 +1,75 @@ +import { Client } from 'minio'; + +import { MinIOHandler } from '../services/uploadHandlers/minio.handler'; + +import { HttpException } from 'src/exceptions/httpException'; + +describe('MinIOHandler', () => { + describe('upload', () => { + it('should upload a file and return the key', async () => { + const file = { + originalname: 'mockFileName.txt', + buffer: Buffer.from('mockFileContent'), + } as Express.Multer.File; + const mockPutObjectFn = jest.fn().mockResolvedValueOnce({}); + const mockClient = { + putObject: mockPutObjectFn, + }; + const handler = new MinIOHandler({ + client: mockClient as unknown as Client, + useSSL: true, + isPrivateRead: true, + expiryTime: '1h', + }); + + const result = await handler.upload(file); + expect(result).toEqual({ + key: expect.stringMatching('.txt'), + }); + expect(mockPutObjectFn).toHaveBeenCalledTimes(1); + }); + + it('should throw an HttpException if upload fails', async () => { + const file = { + originalname: 'mockFileName.txt', + buffer: Buffer.from('mockFileContent'), + } as Express.Multer.File; + const mockPutObjectFn = jest + .fn() + .mockRejectedValueOnce(new Error('Upload failed')); + const mockClient = { + putObject: mockPutObjectFn, + }; + const handler = new MinIOHandler({ + client: mockClient as unknown as Client, + useSSL: true, + isPrivateRead: true, + expiryTime: '1h', + }); + + await expect(handler.upload(file)).rejects.toThrow(HttpException); + }); + }); + + describe('getUrl', () => { + it('should return the URL for the given key', async () => { + const key = 'mockFilePath/mockFileName.txt'; + const handler = new MinIOHandler({ + accessKey: 'mockAccessKey', + secretKey: 'mockSecretKey', + bucket: 'xiaojusurvey', + region: 'mockRegion', + endPoint: 'mockEndPoint', + useSSL: true, + isPrivateRead: true, + expiryTime: '1h', + }); + + const result = await handler.getUrl(key); + const keyReg = new RegExp(key); + const signatureReg = new RegExp('X-Amz-Signature='); + expect(keyReg.test(result)).toBe(true); + expect(signatureReg.test(result)).toBe(true); + }); + }); +}); diff --git a/server/src/modules/file/__test/qiniu.handler.spec.ts b/server/src/modules/file/__test/qiniu.handler.spec.ts new file mode 100644 index 00000000..338b609f --- /dev/null +++ b/server/src/modules/file/__test/qiniu.handler.spec.ts @@ -0,0 +1,102 @@ +// In your test file +import { QiniuHandler } from '../services/uploadHandlers/qiniu.handler'; +import qiniu from 'qiniu'; + +jest.mock('qiniu', () => ({ + auth: { + digest: { + Mac: jest.fn(), + }, + }, + conf: { + Config: jest.fn(), + Zone: jest.fn(), + }, + form_up: { + FormUploader: jest.fn().mockImplementation(() => { + return { + put: jest + .fn() + .mockImplementation( + (uploadToken, key, buffer, putExtra, callback) => { + callback && callback(null, null, { statusCode: 200 }); + }, + ), + }; + }), + PutExtra: jest.fn(), + }, + rs: { + PutPolicy: jest.fn().mockImplementation(() => { + return { + uploadToken: jest.fn(), + }; + }), + BucketManager: jest.fn().mockImplementation(() => { + return { + privateDownloadUrl: jest.fn().mockReturnValue(''), + publicDownloadUrl: jest.fn().mockReturnValue(''), + }; + }), + }, +})); + +describe('QiniuHandler', () => { + describe('upload', () => { + it('should upload a file and return the key', async () => { + const file = { + originalname: 'mockFileName.txt', + buffer: Buffer.from('mockFileContent'), + } as Express.Multer.File; + + const mockMacInstance = { + sign: jest.fn(), + }; + jest + .spyOn(qiniu.auth.digest, 'Mac') + .mockImplementation(() => mockMacInstance as any); + + // Create a new instance of QiniuHandler + const handler = new QiniuHandler({ + accessKey: 'mockAccessKey', + secretKey: 'mockSecretKey', + bucket: 'mockBucket', + endPoint: 'mockEndPoint', + useSSL: true, + isPrivateRead: true, + expiryTime: '1h', + }); + + const result = await handler.upload(file); + + expect(result).toEqual({ + key: expect.stringMatching('.txt'), + }); + + expect(qiniu.auth.digest.Mac).toHaveBeenCalledWith( + 'mockAccessKey', + 'mockSecretKey', + ); + + expect(qiniu.form_up.FormUploader).toHaveBeenCalled(); + }); + }); + + describe('getUrl', () => { + it('should return the URL for the given key', async () => { + const key = 'mockFilePath/mockFileName.txt'; + const handler = new QiniuHandler({ + accessKey: 'mockAccessKey', + secretKey: 'mockSecretKey', + bucket: 'xiaojusurvey', + endPoint: 'mockEndPoint', + useSSL: true, + isPrivateRead: true, + expiryTime: '1h', + }); + + const result = await handler.getUrl(key); + expect(typeof result).toBe('string'); + }); + }); +}); diff --git a/server/src/modules/file/controllers/file.controller.ts b/server/src/modules/file/controllers/file.controller.ts index 349a70ce..6518bb32 100644 --- a/server/src/modules/file/controllers/file.controller.ts +++ b/server/src/modules/file/controllers/file.controller.ts @@ -24,26 +24,6 @@ export class FileController { private readonly configService: ConfigService, ) {} - private getPathPrefix(fileKeyPrefix: string, data: Record) { - const regex = /\{([^}]*)\}/g; - let matches; - const keys = []; - while ((matches = regex.exec(fileKeyPrefix)) !== null) { - keys.push(matches[1]); - } - let result = fileKeyPrefix; - for (const key of keys) { - if (!data[key]) { - throw new HttpException( - `参数有误:${data[key]}`, - EXCEPTION_CODE.PARAMETER_ERROR, - ); - } - result = result.replace(new RegExp(`{${key}}`), data[key]); - } - return result; - } - @Post('upload') @HttpCode(200) @UseInterceptors(FileInterceptor('file')) @@ -56,12 +36,12 @@ export class FileController { if (!channel || !this.configService.get(channel)) { throw new HttpException( - `参数有误:${channel}`, + `参数有误channel不正确:${reqBody.channel}`, EXCEPTION_CODE.PARAMETER_ERROR, ); } const configKey = this.configService.get(channel); - const needAuth = this.configService.get(configKey); + const needAuth = this.configService.get(`${configKey}.NEED_AUTH`); if (needAuth) { const token = req.headers.authorization?.split(' ')[1]; diff --git a/server/src/modules/file/services/file.service.ts b/server/src/modules/file/services/file.service.ts index b499b3b1..fb89ae1b 100644 --- a/server/src/modules/file/services/file.service.ts +++ b/server/src/modules/file/services/file.service.ts @@ -10,10 +10,6 @@ import { LocalHandler } from './uploadHandlers/local.handler'; export class FileService { constructor(private readonly configService: ConfigService) {} - private getConfig(channel, key) { - return this.configService.get(channel + '_' + key); - } - async upload({ configKey, file, diff --git a/server/src/modules/file/services/uploadHandlers/alioss.handler.ts b/server/src/modules/file/services/uploadHandlers/alioss.handler.ts index 0703f9d6..dc5daf77 100644 --- a/server/src/modules/file/services/uploadHandlers/alioss.handler.ts +++ b/server/src/modules/file/services/uploadHandlers/alioss.handler.ts @@ -11,6 +11,7 @@ export class AliOssHandler implements FileUploadHandler { isPrivateRead: boolean; expiryTime: string; constructor({ + client, accessKey, secretKey, bucket, @@ -19,13 +20,25 @@ export class AliOssHandler implements FileUploadHandler { useSSL, isPrivateRead, expiryTime, + }: { + client?: OSS; + accessKey?: string; + secretKey?: string; + bucket?: string; + region?: string; + endPoint?: string; + useSSL?: boolean; + isPrivateRead?: boolean; + expiryTime?: string; }) { - const client = new OSS({ - region, - accessKeyId: accessKey, - accessKeySecret: secretKey, - bucket, - }); + if (!client) { + client = new OSS({ + region, + accessKeyId: accessKey, + accessKeySecret: secretKey, + bucket, + }); + } this.client = client; this.endPoint = endPoint; this.useSSL = useSSL; diff --git a/server/src/modules/file/services/uploadHandlers/minio.handler.ts b/server/src/modules/file/services/uploadHandlers/minio.handler.ts index 1891c1f6..d37c12cc 100644 --- a/server/src/modules/file/services/uploadHandlers/minio.handler.ts +++ b/server/src/modules/file/services/uploadHandlers/minio.handler.ts @@ -14,6 +14,7 @@ export class MinIOHandler implements FileUploadHandler { expiryTime: string; bucket: string; constructor({ + client, accessKey, secretKey, bucket, @@ -22,15 +23,27 @@ export class MinIOHandler implements FileUploadHandler { useSSL, isPrivateRead, expiryTime, + }: { + client?: Client; + accessKey?: string; + secretKey?: string; + bucket?: string; + region?: string; + endPoint?: string; + useSSL?: boolean; + isPrivateRead?: boolean; + expiryTime?: string; }) { - const client = new Client({ - endPoint, - accessKey, - secretKey, - region, - useSSL, - pathStyle: true, - }); + if (!client) { + client = new Client({ + endPoint, + accessKey, + secretKey, + region, + useSSL, + pathStyle: true, + }); + } this.client = client; this.endPoint = endPoint; this.useSSL = useSSL; diff --git a/server/src/modules/file/utils/replaceFileKey.ts b/server/src/modules/file/utils/replaceFileKey.ts deleted file mode 100644 index 56c26e0d..00000000 --- a/server/src/modules/file/utils/replaceFileKey.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const replaceFileKey = ( - originStr: string, - arr: Array<{ key; value }>, -) => { - let retStr = originStr; - for (const { key, value } of arr) { - if (value) { - retStr = retStr.replace(new RegExp(`{${key}}`), value); - } - } - return retStr; -}; diff --git a/server/src/modules/message/__test/messagePushingTask.controller.spec.ts b/server/src/modules/message/__test/messagePushingTask.controller.spec.ts index bf0bc0e8..49afdaa3 100644 --- a/server/src/modules/message/__test/messagePushingTask.controller.spec.ts +++ b/server/src/modules/message/__test/messagePushingTask.controller.spec.ts @@ -5,7 +5,6 @@ import { MessagePushingTaskService } from '../services/messagePushingTask.servic import { CreateMessagePushingTaskDto } from '../dto/createMessagePushingTask.dto'; import { QueryMessagePushingTaskListDto } from '../dto/queryMessagePushingTaskList.dto'; import { HttpException } from 'src/exceptions/httpException'; -import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { MESSAGE_PUSHING_HOOK, MESSAGE_PUSHING_TYPE, @@ -16,6 +15,7 @@ import { UserService } from 'src/modules/auth/services/user.service'; import { UpdateMessagePushingTaskDto } from '../dto/updateMessagePushingTask.dto'; import { ObjectId } from 'mongodb'; +import { AuthService } from 'src/modules/auth/services/auth.service'; describe('MessagePushingTaskController', () => { let controller: MessagePushingTaskController; @@ -51,6 +51,14 @@ describe('MessagePushingTaskController', () => { }, })), }, + { + provide: AuthService, + useClass: jest.fn().mockImplementation(() => ({ + verifyToken() { + return {}; + }, + })), + }, ], }).compile(); @@ -137,9 +145,7 @@ describe('MessagePushingTaskController', () => { await expect( controller.findAll(req, queryDto as QueryMessagePushingTaskListDto), - ).rejects.toThrow( - new HttpException('参数错误', EXCEPTION_CODE.PARAMETER_ERROR), - ); + ).rejects.toThrow(HttpException); expect(service.findAll).toHaveBeenCalledTimes(0); }); }); diff --git a/server/src/modules/message/__test/messagePushingTask.service.spec.ts b/server/src/modules/message/__test/messagePushingTask.service.spec.ts index 4cf9a834..38d8852c 100644 --- a/server/src/modules/message/__test/messagePushingTask.service.spec.ts +++ b/server/src/modules/message/__test/messagePushingTask.service.spec.ts @@ -65,7 +65,6 @@ describe('MessagePushingTaskService', () => { savedTask.type = MESSAGE_PUSHING_TYPE.HTTP; savedTask.pushAddress = 'http://example.com'; savedTask.triggerHook = MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED; - savedTask.surveys = ['surveyId1', 'surveyId2']; const mockOwnerId = '66028642292c50f8b71a9eee'; diff --git a/server/src/modules/message/dto/createMessagePushingTask.dto.ts b/server/src/modules/message/dto/createMessagePushingTask.dto.ts index 5e1d0ca6..685c57ba 100644 --- a/server/src/modules/message/dto/createMessagePushingTask.dto.ts +++ b/server/src/modules/message/dto/createMessagePushingTask.dto.ts @@ -37,10 +37,7 @@ export class CreateMessagePushingTaskDto { triggerHook: Joi.string() .allow(null) .default(MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED), - surveys: Joi.array() - .items(Joi.string().required()) - .allow(null) - .default([]), + surveys: Joi.array().items(Joi.string()).allow(null).default([]), }).validateAsync(data); } } diff --git a/server/src/modules/survey/__test/dataStatistic.controller.spec.ts b/server/src/modules/survey/__test/dataStatistic.controller.spec.ts index 428ccadc..5cd3247d 100644 --- a/server/src/modules/survey/__test/dataStatistic.controller.spec.ts +++ b/server/src/modules/survey/__test/dataStatistic.controller.spec.ts @@ -12,6 +12,7 @@ import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager'; import { Authtication } from 'src/guards/authtication'; import { UserService } from 'src/modules/auth/services/user.service'; import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin'; +import { AuthService } from 'src/modules/auth/services/auth.service'; jest.mock('../services/dataStatistic.service'); jest.mock('../services/surveyMeta.service'); @@ -45,6 +46,14 @@ describe('DataStatisticController', () => { }, })), }, + { + provide: AuthService, + useClass: jest.fn().mockImplementation(() => ({ + varifytoken() { + return {}; + }, + })), + }, ], }).compile(); diff --git a/server/src/modules/survey/__test/surveyHistory.controller.spec.ts b/server/src/modules/survey/__test/surveyHistory.controller.spec.ts index 1ae46fec..023385e6 100644 --- a/server/src/modules/survey/__test/surveyHistory.controller.spec.ts +++ b/server/src/modules/survey/__test/surveyHistory.controller.spec.ts @@ -1,11 +1,13 @@ import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigService } from '@nestjs/config'; + import { SurveyHistoryController } from '../controllers/surveyHistory.controller'; import { SurveyHistoryService } from '../services/surveyHistory.service'; import { SurveyMetaService } from '../services/surveyMeta.service'; + import { UserService } from 'src/modules/auth/services/user.service'; import { Authtication } from 'src/guards/authtication'; - -import { ConfigService } from '@nestjs/config'; +import { AuthService } from 'src/modules/auth/services/auth.service'; describe('SurveyHistoryController', () => { let controller: SurveyHistoryController; @@ -43,6 +45,14 @@ describe('SurveyHistoryController', () => { }, })), }, + { + provide: AuthService, + useClass: jest.fn().mockImplementation(() => ({ + verifyToken() { + return {}; + }, + })), + }, ], }).compile();