Skip to content

Commit

Permalink
feat(server): support function rename (#1336)
Browse files Browse the repository at this point in the history
* feat(server): make function service support function rename

* chore

* chore(server):

* chore(server): Add function function trigger handling measures

* chore()

* chore
  • Loading branch information
HUAHUAI23 authored Jul 4, 2023
1 parent ad4d8ab commit fd8292d
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 6 deletions.
17 changes: 16 additions & 1 deletion server/src/function/dto/update-function.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'
import { IsArray, IsIn, IsNotEmpty, IsString, MaxLength } from 'class-validator'
import {
IsArray,
IsIn,
IsNotEmpty,
IsOptional,
IsString,
Matches,
MaxLength,
} from 'class-validator'
import { HTTP_METHODS } from '../../constants'
import { HttpMethod } from '../entities/cloud-function'

export class UpdateFunctionDto {
@ApiProperty({
description: 'Function name is unique in the application',
})
@IsOptional()
@Matches(/^[a-zA-Z0-9_.\-\/]{1,256}$/)
newName?: string

@ApiPropertyOptional()
@MaxLength(256)
description: string
Expand Down
5 changes: 4 additions & 1 deletion server/src/function/function.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,14 @@ export class FunctionController {
HttpStatus.NOT_FOUND,
)
}

const res = await this.functionsService.updateOne(func, dto)
if (!res) {
return ResponseUtil.error(i18n.t('function.update.error'))
}
if (res instanceof Error) {
return ResponseUtil.error(res.message)
}

return ResponseUtil.ok(res)
}

Expand Down
3 changes: 2 additions & 1 deletion server/src/function/function.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { ApplicationModule } from '../application/application.module'
import { FunctionController } from './function.controller'
import { FunctionService } from './function.service'
import { DatabaseModule } from 'src/database/database.module'
import { TriggerService } from 'src/trigger/trigger.service'

@Module({
imports: [ApplicationModule, DatabaseModule],
controllers: [FunctionController],
providers: [FunctionService, JwtService],
providers: [FunctionService, JwtService, TriggerService],
exports: [FunctionService],
})
export class FunctionModule {}
78 changes: 76 additions & 2 deletions server/src/function/function.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
APPLICATION_SECRET_KEY,
CN_FUNCTION_LOGS,
CN_PUBLISHED_FUNCTIONS,
TASK_LOCK_INIT_TIME,
} from '../constants'
import { CreateFunctionDto } from './dto/create-function.dto'
import { UpdateFunctionDto } from './dto/update-function.dto'
Expand All @@ -18,6 +19,8 @@ import { CloudFunction } from './entities/cloud-function'
import { ApplicationConfiguration } from 'src/application/entities/application-configuration'
import { FunctionLog } from 'src/log/entities/function-log'
import { CloudFunctionHistory } from './entities/cloud-function-history'
import { TriggerService } from 'src/trigger/trigger.service'
import { TriggerPhase } from 'src/trigger/entities/cron-trigger'
import { UpdateFunctionDebugDto } from './dto/update-function-debug.dto'

@Injectable()
Expand All @@ -28,6 +31,7 @@ export class FunctionService {
constructor(
private readonly databaseService: DatabaseService,
private readonly jwtService: JwtService,
private readonly triggerService: TriggerService,
) {}
async create(appid: string, userid: ObjectId, dto: CreateFunctionDto) {
await this.db.collection<CloudFunction>('CloudFunction').insertOne({
Expand Down Expand Up @@ -79,6 +83,73 @@ export class FunctionService {
}

async updateOne(func: CloudFunction, dto: UpdateFunctionDto) {
// update function name
if (dto.newName) {
const client = SystemDatabase.client
const session = client.startSession()

const found = await this.findOne(func.appid, dto.newName)
if (found) {
return new Error(`Function name ${found.name} already exists`)
}

try {
session.startTransaction()

const fn = await this.db
.collection<CloudFunction>('CloudFunction')
.findOneAndUpdate(
{ appid: func.appid, name: func.name },
{
$set: {
name: dto.newName,
desc: dto.description,
methods: dto.methods,
tags: dto.tags || [],
updatedAt: new Date(),
},
},
{ session, returnDocument: 'after' },
)

// publish
await this.publish(fn.value, func.name)

// trigger
const triggers = await this.triggerService.findAllByTarget(
func.appid,
func.name,
)
if (triggers.length !== 0) {
const triggersToInsert = triggers.map((doc) => ({
appid: doc.appid,
desc: doc.desc,
cron: doc.cron,
target: dto.newName, // set to new function name
state: doc.state,
phase: TriggerPhase.Creating,
lockedAt: TASK_LOCK_INIT_TIME,
createdAt: new Date(doc.createdAt),
updatedAt: new Date(),
}))
await this.triggerService.removeAllByTarget(
func.appid,
func.name,
session,
)
await this.triggerService.createMany(triggersToInsert, session)
}
await session.commitTransaction()
return fn.value
} catch (error) {
await session.abortTransaction()
this.logger.error(error)
throw error
} finally {
await session.endSession()
}
}

await this.db.collection<CloudFunction>('CloudFunction').updateOne(
{ appid: func.appid, name: func.name },
{
Expand Down Expand Up @@ -138,13 +209,16 @@ export class FunctionService {
return res
}

async publish(func: CloudFunction) {
async publish(func: CloudFunction, oldFuncName?: string) {
const { db, client } = await this.databaseService.findAndConnect(func.appid)
const session = client.startSession()
try {
await session.withTransaction(async () => {
const coll = db.collection(CN_PUBLISHED_FUNCTIONS)
await coll.deleteOne({ name: func.name }, { session })
await coll.deleteOne(
{ name: oldFuncName ? oldFuncName : func.name },
{ session },
)
await coll.insertOne(func, { session })
})
} finally {
Expand Down
8 changes: 8 additions & 0 deletions server/src/trigger/trigger.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { BundleService } from 'src/application/bundle.service'
import { ObjectId } from 'mongodb'
import { JwtAuthGuard } from 'src/authentication/jwt.auth.guard'
import { ApplicationAuthGuard } from 'src/authentication/application.auth.guard'
import { FunctionService } from 'src/function/function.service'

@ApiTags('Trigger')
@Controller('apps/:appid/triggers')
Expand All @@ -30,6 +31,7 @@ export class TriggerController {
constructor(
private readonly triggerService: TriggerService,
private readonly bundleService: BundleService,
private readonly funcService: FunctionService,
) {}

/**
Expand Down Expand Up @@ -57,6 +59,12 @@ export class TriggerController {
return ResponseUtil.error('Invalid cron expression')
}

// Check if the target function exists
const found = await this.funcService.findOne(appid, dto.target)
if (!found) {
return ResponseUtil.error("Target function doesn't exist")
}

const res = await this.triggerService.create(appid, dto)
return ResponseUtil.ok(res)
}
Expand Down
45 changes: 44 additions & 1 deletion server/src/trigger/trigger.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
TriggerPhase,
TriggerState,
} from './entities/cron-trigger'
import { ObjectId } from 'mongodb'
import { ClientSession, ObjectId } from 'mongodb'

@Injectable()
export class TriggerService {
Expand Down Expand Up @@ -73,6 +73,49 @@ export class TriggerService {
return res
}

async findAllByTarget(appid: string, target: string) {
const docs = await this.db
.collection<CronTrigger>('CronTrigger')
.find({ appid, target })
.toArray()
return docs
}

async createMany(docs: CronTrigger[], session?: ClientSession) {
if (session) {
const result = await this.db
.collection<CronTrigger>('CronTrigger')
.insertMany(docs, { session })
return result
}
const result = await this.db
.collection<CronTrigger>('CronTrigger')
.insertMany(docs)
return result
}

async removeAllByTarget(
appid: string,
target: string,
session?: ClientSession,
) {
if (session) {
const res = await this.db
.collection<CronTrigger>('CronTrigger')
.updateMany(
{ appid, target },
{ $set: { state: TriggerState.Deleted } },
{ session },
)
return res
}
const res = await this.db
.collection<CronTrigger>('CronTrigger')
.updateMany({ appid, target }, { $set: { state: TriggerState.Deleted } })

return res
}

isValidCronExpression(cron: string) {
const ret = CronValidate(cron)
if (ret.isValid()) {
Expand Down

0 comments on commit fd8292d

Please sign in to comment.