From f2970020c0c33d9802942c4105cec27f03fbf20a Mon Sep 17 00:00:00 2001 From: Maslow Date: Tue, 29 Nov 2022 19:19:03 +0800 Subject: [PATCH] feat(server): impl cloud function update & deletetion apis (#451) Signed-off-by: maslow Signed-off-by: maslow --- server/src/app.controller.ts | 4 +- .../applications/applications.controller.ts | 10 ++- server/src/buckets/buckets.controller.ts | 9 ++- .../src/functions/dto/create-function.dto.ts | 20 +++--- .../src/functions/dto/update-function.dto.ts | 35 +++++++++- server/src/functions/functions.controller.ts | 69 ++++++++++++++++--- server/src/functions/functions.service.ts | 35 ++++++++-- 7 files changed, 146 insertions(+), 36 deletions(-) diff --git a/server/src/app.controller.ts b/server/src/app.controller.ts index cd991179a6..c3b9853c76 100644 --- a/server/src/app.controller.ts +++ b/server/src/app.controller.ts @@ -47,9 +47,7 @@ export class AppController { * @returns */ @ApiOperation({ summary: 'Get user token by auth code' }) - @ApiResponse({ - type: ResponseUtil, - }) + @ApiResponse({ type: ResponseUtil }) @Get('code2token') async code2token(@Query('code') code: string) { const token = await this.authService.code2token(code) diff --git a/server/src/applications/applications.controller.ts b/server/src/applications/applications.controller.ts index fbba4d9b1b..845552f106 100644 --- a/server/src/applications/applications.controller.ts +++ b/server/src/applications/applications.controller.ts @@ -11,7 +11,12 @@ import { HttpException, HttpStatus, } from '@nestjs/common' -import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger' +import { + ApiBearerAuth, + ApiOperation, + ApiResponse, + ApiTags, +} from '@nestjs/swagger' import { IRequest } from 'src/common/types' import { JwtAuthGuard } from '../auth/jwt-auth.guard' import { ApiResponseUtil, ResponseUtil } from '../common/response' @@ -125,9 +130,10 @@ export class ApplicationsController { * Delete an application * @returns */ + @ApiResponse({ type: ResponseUtil }) + @ApiOperation({ summary: 'Delete an application' }) @Delete(':appid') @UseGuards(JwtAuthGuard, ApplicationAuthGuard) - @ApiOperation({ summary: 'Delete an application' }) async remove(@Param('appid') _appid: string, @Req() req: IRequest) { const app = req.application const res = await this.appService.remove(app) diff --git a/server/src/buckets/buckets.controller.ts b/server/src/buckets/buckets.controller.ts index fcc89c190b..4d72b2ba8a 100644 --- a/server/src/buckets/buckets.controller.ts +++ b/server/src/buckets/buckets.controller.ts @@ -12,7 +12,12 @@ import { HttpException, HttpStatus, } from '@nestjs/common' -import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger' +import { + ApiBearerAuth, + ApiOperation, + ApiResponse, + ApiTags, +} from '@nestjs/swagger' import { ApplicationAuthGuard } from 'src/applications/application.auth.guard' import { IRequest } from 'src/common/types' import { JwtAuthGuard } from '../auth/jwt-auth.guard' @@ -135,7 +140,7 @@ export class BucketsController { * @param name bucket name * @returns */ - @ApiResponseUtil(Bucket) + @ApiResponse({ type: ResponseUtil }) @UseGuards(JwtAuthGuard, ApplicationAuthGuard) @ApiOperation({ summary: 'Delete a bucket' }) @Delete(':name') diff --git a/server/src/functions/dto/create-function.dto.ts b/server/src/functions/dto/create-function.dto.ts index cd413a7739..5136d24f93 100644 --- a/server/src/functions/dto/create-function.dto.ts +++ b/server/src/functions/dto/create-function.dto.ts @@ -1,5 +1,12 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' -import { IsIn, IsNotEmpty, IsString, Length, MaxLength } from 'class-validator' +import { + IsBoolean, + IsIn, + IsNotEmpty, + IsString, + Length, + MaxLength, +} from 'class-validator' import { HTTP_METHODS } from 'src/constants' export class CreateFunctionDto { @@ -15,10 +22,11 @@ export class CreateFunctionDto { description: string @ApiProperty() + @IsBoolean() websocket: boolean @ApiProperty({ type: [String], enum: HTTP_METHODS }) - @IsIn(HTTP_METHODS) + @IsIn(HTTP_METHODS, { each: true }) methods: string[] = [] @ApiProperty({ description: 'The source code of the function' }) @@ -28,14 +36,6 @@ export class CreateFunctionDto { codes: string validate() { - if (!this.methods) { - this.methods = [] - } - const valid = this.methods.every((method) => HTTP_METHODS.includes(method)) - if (!valid) { - return 'methods is invalid' - } - return null } } diff --git a/server/src/functions/dto/update-function.dto.ts b/server/src/functions/dto/update-function.dto.ts index 7c766bb9d2..e800222176 100644 --- a/server/src/functions/dto/update-function.dto.ts +++ b/server/src/functions/dto/update-function.dto.ts @@ -1,4 +1,33 @@ -import { PartialType } from '@nestjs/mapped-types' -import { CreateFunctionDto } from './create-function.dto' +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' +import { + IsBoolean, + IsIn, + IsNotEmpty, + IsString, + MaxLength, +} from 'class-validator' +import { HTTP_METHODS } from 'src/constants' -export class UpdateFunctionDto extends PartialType(CreateFunctionDto) {} +export class UpdateFunctionDto { + @ApiPropertyOptional() + @MaxLength(256) + description: string + + @ApiProperty() + @IsBoolean() + websocket: boolean + + @ApiProperty({ type: [String], enum: HTTP_METHODS }) + @IsIn(HTTP_METHODS, { each: true }) + methods: string[] = [] + + @ApiProperty({ description: 'The source code of the function' }) + @IsNotEmpty() + @IsString() + @MaxLength(1024 * 512) + codes: string + + validate() { + return null + } +} diff --git a/server/src/functions/functions.controller.ts b/server/src/functions/functions.controller.ts index c6e21faccb..fa9ddf116c 100644 --- a/server/src/functions/functions.controller.ts +++ b/server/src/functions/functions.controller.ts @@ -15,7 +15,12 @@ import { CreateFunctionDto } from './dto/create-function.dto' import { UpdateFunctionDto } from './dto/update-function.dto' import { ApiResponseUtil, ResponseUtil } from 'src/common/response' import { CloudFunction, CloudFunctionList } from './entities/function.entity' -import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger' +import { + ApiBearerAuth, + ApiOperation, + ApiResponse, + ApiTags, +} from '@nestjs/swagger' import { JwtAuthGuard } from 'src/auth/jwt-auth.guard' import { ApplicationAuthGuard } from 'src/applications/application.auth.guard' @@ -40,6 +45,12 @@ export class FunctionsController { return ResponseUtil.error(error) } + // check name is unique + const found = await this.functionsService.findOne(appid, dto.name) + if (found) { + return ResponseUtil.error('function name is already existed') + } + const res = await this.functionsService.create(appid, dto) if (!res) { return ResponseUtil.error('create function error') @@ -77,18 +88,54 @@ export class FunctionsController { return ResponseUtil.ok(data) } - @Patch(':id') - @ApiOperation({ summary: 'TODO - ⌛️' }) - update( - @Param('id') id: string, - @Body() updateFunctionDto: UpdateFunctionDto, + /** + * Update a function + * @param appid + * @param name + * @param dto + * @returns + */ + @ApiResponseUtil(CloudFunction) + @ApiOperation({ summary: 'Update a function' }) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Patch(':name') + async update( + @Param('appid') appid: string, + @Param('name') name: string, + @Body() dto: UpdateFunctionDto, ) { - return this.functionsService.update(+id, updateFunctionDto) + const func = await this.functionsService.findOne(appid, name) + if (!func) { + throw new HttpException('function not found', HttpStatus.NOT_FOUND) + } + + const res = await this.functionsService.update(func, dto) + if (!res) { + return ResponseUtil.error('update function error') + } + return ResponseUtil.ok(res) } - @ApiOperation({ summary: 'TODO - ⌛️' }) - @Delete(':id') - remove(@Param('id') id: string) { - return this.functionsService.remove(+id) + /** + * Delete a function + * @param appid + * @param name + * @returns + */ + @ApiResponse({ type: ResponseUtil }) + @ApiOperation({ summary: 'Delete a function' }) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Delete(':name') + async remove(@Param('appid') appid: string, @Param('name') name: string) { + const func = await this.functionsService.findOne(appid, name) + if (!func) { + throw new HttpException('function not found', HttpStatus.NOT_FOUND) + } + + const res = await this.functionsService.remove(func) + if (!res) { + return ResponseUtil.error('delete function error') + } + return ResponseUtil.ok(res) } } diff --git a/server/src/functions/functions.service.ts b/server/src/functions/functions.service.ts index 3f3170bfbf..f1423cc196 100644 --- a/server/src/functions/functions.service.ts +++ b/server/src/functions/functions.service.ts @@ -27,7 +27,7 @@ export class FunctionsService { try { const res = await this.k8sClient.objectApi.create(func) - return res.body + return CloudFunction.fromObject(res.body) } catch (error) { this.logger.error(error, error?.response?.body) return null @@ -83,11 +83,36 @@ export class FunctionsService { } } - update(id: number, updateFunctionDto: UpdateFunctionDto) { - return `This action updates a #${id} function` + async update(func: CloudFunction, dto: UpdateFunctionDto) { + if (dto.description) { + func.spec.description = dto.description + } + if (dto.methods) { + func.spec.methods = dto.methods + } + if (dto.codes) { + func.spec.source.codes = dto.codes + } + if (dto.websocket) { + func.spec.websocket = dto.websocket + } + + try { + const res = await this.k8sClient.patchCustomObject(func) + return CloudFunction.fromObject(res) + } catch (error) { + this.logger.error(error, error?.response?.body) + return null + } } - remove(id: number) { - return `This action removes a #${id} function` + async remove(func: CloudFunction) { + try { + const res = await this.k8sClient.deleteCustomObject(func) + return CloudFunction.fromObject(res) + } catch (error) { + this.logger.error(error, error?.response?.body) + return null + } } }