Skip to content

feat(runtime): reuse context #1539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cloud-sdk/src/function.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface FunctionContext {
request?: Request
response?: Response
__function_name?: string
[key: string]: any
}

/**
Expand Down
44 changes: 15 additions & 29 deletions runtimes/nodejs/src/handler/debug-func.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
import { Response } from 'express'
import { FunctionContext } from '../support/function-engine'
import { logger } from '../support/logger'
import { CloudFunction } from '../support/function-engine'
import { IRequest } from '../support/types'
import { parseToken } from '../support/token'
import { ICloudFunctionData } from '@lafjs/cloud'

/**
* Handler of debugging cloud function
*/
export async function handleDebugFunction(req: IRequest, res: Response) {
export async function handleDebugFunction(ctx: FunctionContext) {
// verify the debug token
const token = req.get('x-laf-develop-token')
const token = ctx.request.get('x-laf-develop-token')
if (!token) {
return res.status(400).send('x-laf-develop-token is required')
return ctx.response.status(400).send('x-laf-develop-token is required')
}
const auth = parseToken(token) || null
if (auth?.type !== 'develop') {
return res.status(403).send('permission denied: invalid develop token')
return ctx.response.status(403).send('permission denied: invalid develop token')
}

// get func_data from header
const func_str = req.get('x-laf-func-data')
const func_str = ctx.request.get('x-laf-func-data')
if (!func_str) {
return res.status(400).send('x-laf-func-data is required')
return ctx.response.status(400).send('x-laf-func-data is required')
}

// parse func_data
Expand All @@ -32,38 +30,26 @@ export async function handleDebugFunction(req: IRequest, res: Response) {
const decoded = decodeURIComponent(func_str)
func_data = JSON.parse(decoded)
} catch (error) {
return res.status(400).send('x-laf-func-data is invalid')
return ctx.response.status(400).send('x-laf-func-data is invalid')
}

const requestId = req['requestId']
const func_name = req.params?.name
const requestId = ctx.requestId
const func_name = ctx.request.params?.name

if (!func_data) {
return res.send({ code: 1, error: 'function data not found', requestId })
return ctx.response.send({ code: 1, error: 'function data not found', requestId })
}

const func = new CloudFunction(func_data)

try {
// execute the func
const ctx: FunctionContext = {
query: req.query,
files: req.files as any,
body: req.body,
headers: req.headers,
method: req.method,
auth: req.user,
user: req.user,
requestId,
request: req,
response: res,
__function_name: func.name,
}
ctx.__function_name = func_name
const result = await func.invoke(ctx)

if (result.error) {
logger.error(requestId, `debug function ${func_name} error: `, result)
return res.send({
return ctx.response.send({
error: 'invoke function got error: ' + result.error.toString(),
time_usage: result.time_usage,
requestId,
Expand All @@ -72,15 +58,15 @@ export async function handleDebugFunction(req: IRequest, res: Response) {

logger.trace(requestId, `invoke ${func_name} invoke success: `, result)

if (res.writableEnded === false) {
if (ctx.response.writableEnded === false) {
let data = result.data
if (typeof result.data === 'number') {
data = Number(result.data).toString()
}
return res.send(data)
return ctx.response.send(data)
}
} catch (error) {
logger.error(requestId, 'failed to invoke error', error)
return res.status(500).send('Internal Server Error')
return ctx.response.status(500).send('Internal Server Error')
}
}
60 changes: 26 additions & 34 deletions runtimes/nodejs/src/handler/invoke-func.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,30 @@ import { DEFAULT_FUNCTION_NAME, INTERCEPTOR_FUNCTION_NAME } from '../constants'
* Handler of invoking cloud function
*/
export async function handleInvokeFunction(req: IRequest, res: Response) {

const ctx: FunctionContext = {
requestId: req.requestId,
query: req.query,
files: req.files as any,
body: req.body,
headers: req.headers,
method: req.method,
auth: req['auth'],
user: req.user,
request: req,
response: res,
varMap: new Map(),
}

// intercept the request, skip websocket request
if (false === req.method.startsWith('WebSocket:')) {
const passed = await invokeInterceptor(req, res)
const passed = await invokeInterceptor(ctx)
if (passed === false) return
}

// debug mode
if (req.get('x-laf-develop-token')) {
return await handleDebugFunction(req, res)
return await handleDebugFunction(ctx)
}

// trigger mode
Expand Down Expand Up @@ -50,19 +65,7 @@ export async function handleInvokeFunction(req: IRequest, res: Response) {

try {
// execute the func
const ctx: FunctionContext = {
query: req.query,
files: req.files as any,
body: req.body,
headers: req.headers,
method: isTrigger ? 'trigger' : req.method,
auth: req['auth'],
user: req.user,
requestId,
request: req,
response: res,
__function_name: func.name,
}
ctx.__function_name = func.name
const result = await func.invoke(ctx)

if (result.error) {
Expand Down Expand Up @@ -98,9 +101,10 @@ export async function handleInvokeFunction(req: IRequest, res: Response) {
}
}

async function invokeInterceptor(req: IRequest, res: Response) {
const requestId = req.requestId
async function invokeInterceptor(ctx: FunctionContext) {

const func_name = INTERCEPTOR_FUNCTION_NAME
const requestId = ctx.requestId

// load function data from db
const funcData = CloudFunction.getFunctionByName(func_name)
Expand All @@ -113,19 +117,7 @@ async function invokeInterceptor(req: IRequest, res: Response) {

try {
// execute the func
const ctx: FunctionContext = {
query: req.query,
files: req.files as any,
body: req.body,
headers: req.headers,
method: req.method,
auth: req['auth'],
user: req.user,
requestId,
request: req,
response: res,
__function_name: func.name,
}
ctx.__function_name = func.name
const result = await func.invoke(ctx)

// return false to reject request if interceptor got error
Expand All @@ -136,7 +128,7 @@ async function invokeInterceptor(req: IRequest, res: Response) {
result,
)

res.status(400).send({
ctx.response.status(400).send({
error: `invoke ${func_name} function got error, please check the function logs`,
requestId,
})
Expand All @@ -145,21 +137,21 @@ async function invokeInterceptor(req: IRequest, res: Response) {
}

// if response has been ended, return false to stop the request
if (res.writableEnded) {
if (ctx.response.writableEnded) {
return false
}

// reject request if interceptor return false
if (false === result.data) {
res.status(403).send({ error: 'Forbidden', requestId })
ctx.response.status(403).send({ error: 'Forbidden', requestId })
return false
}

// pass the request
return result.data
} catch (error) {
logger.error(requestId, `failed to invoke ${func_name}`, error)
return res
return ctx.response
.status(500)
.send(`Internal Server Error - got error in ${func_name}`)
}
Expand Down
3 changes: 2 additions & 1 deletion runtimes/nodejs/src/support/function-engine/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ export interface FunctionContext {
method?: string
socket?: WebSocket
request?: Request
response?: Response
response?: Response
__function_name?: string
[key: string]: any
}

/**
Expand Down