Skip to content
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

fix(runtime): impv runtime logging struct; add func logs api #538

Merged
merged 1 commit into from
Dec 14, 2022
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
115 changes: 58 additions & 57 deletions runtimes/nodejs/src/cloud-sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,69 @@
import { AxiosStatic } from "axios";
import { Db, getDb } from "database-proxy";
import { CloudFunction, FunctionContext } from "../support/function-engine";
import * as mongodb from "mongodb";
import { DatabaseAgent } from "../db";
import request from "axios";
import { SchedulerInstance } from "../support/scheduler";
import { getToken, parseToken } from "../support/token";
import { WebSocket } from "ws";
import { WebSocketAgent } from "../support/ws";
import Config from "../config";
import { AxiosStatic } from 'axios'
import { Db, getDb } from 'database-proxy'
import { CloudFunction, FunctionContext } from '../support/function-engine'
import * as mongodb from 'mongodb'
import { DatabaseAgent } from '../db'
import request from 'axios'
import { SchedulerInstance } from '../support/scheduler'
import { getToken, parseToken } from '../support/token'
import { WebSocket } from 'ws'
import { WebSocketAgent } from '../support/ws'
import Config from '../config'

export type InvokeFunctionType = (
name: string,
param: FunctionContext
) => Promise<any>;
export type EmitFunctionType = (event: string, param: any) => void;
export type GetTokenFunctionType = (payload: any, secret?: string) => string;
param: FunctionContext,
) => Promise<any>
export type EmitFunctionType = (event: string, param: any) => void
export type GetTokenFunctionType = (payload: any, secret?: string) => string
export type ParseTokenFunctionType = (
token: string,
secret?: string
) => any | null;
secret?: string,
) => any | null

export interface MongoDriverObject {
client: mongodb.MongoClient;
db: mongodb.Db;
client: mongodb.MongoClient
db: mongodb.Db
}

export interface CloudSdkInterface {
/**
* Sending an HTTP request is actually an Axios instance. You can refer to the Axios documentation directly
*/
fetch: AxiosStatic;
fetch: AxiosStatic

/**
* Get a laf.js database-ql instance
*/
database(): Db;
database(): Db

/**
* Invoke cloud function
*/
invoke: InvokeFunctionType;
invoke: InvokeFunctionType

/**
* Emit a cloud function event that other cloud functions can set triggers to listen for
*/
emit: EmitFunctionType;
emit: EmitFunctionType

/**
* Cloud function global memory `shared` object, which can share data across multiple requests and different cloud functions
* 1. Some global configurations can be initialized into `shared`, such as 3rd-party API configuration
* 2. You can share some common methods, such as checkPermission(), to improve the performance of cloud functions
* 3. It can cache hot data and is recommended to use it in a small amount (this object is allocated in the node VM heap because of the memory limit of the node VM heap)
*/
shared: Map<string, any>;
shared: Map<string, any>

/**
* Generate a JWT Token, if don't provide `secret` fields, use current server secret key to do signature
*/
getToken: GetTokenFunctionType;
getToken: GetTokenFunctionType

/**
* Parse a JWT Token, if don't provide `secret` fields, use current server secret key to verify signature
*/
parseToken: ParseTokenFunctionType;
parseToken: ParseTokenFunctionType

/**
* The mongodb instance of MongoDB node.js native driver.
Expand Down Expand Up @@ -93,44 +93,44 @@ export interface CloudSdkInterface {
* .toArray()
* ```
*/
mongo: MongoDriverObject;
mongo: MongoDriverObject

/**
* Websocket connection list
*/
sockets: Set<WebSocket>;
sockets: Set<WebSocket>

/**
* Current app id
*/
appid: string;
appid: string

env: {
DB_URI?: string;
SERVER_SECRET?: string;
APP_ID?: string;
OSS_ACCESS_KEY?: string;
OSS_ACCESS_SECRET?: string;
OSS_REGION?: string;
OSS_INTERNAL_ENDPOINT?: string;
OSS_EXTERNAL_ENDPOINT?: string;
NPM_INSTALL_FLAGS?: string;
RUNTIME_IMAGE?: string;
};
DB_URI?: string
SERVER_SECRET?: string
APP_ID?: string
OSS_ACCESS_KEY?: string
OSS_ACCESS_SECRET?: string
OSS_REGION?: string
OSS_INTERNAL_ENDPOINT?: string
OSS_EXTERNAL_ENDPOINT?: string
NPM_INSTALL_FLAGS?: string
RUNTIME_IMAGE?: string
}
}

/**
* Cloud SDK instance
*/
const cloud: CloudSdkInterface = create();
const cloud: CloudSdkInterface = create()

/**
* After the database connection is successful, update its Mongo object, otherwise it is null
*/
DatabaseAgent.accessor.ready.then(() => {
cloud.mongo.client = DatabaseAgent.accessor.conn;
cloud.mongo.db = DatabaseAgent.accessor.db;
});
cloud.mongo.client = DatabaseAgent.accessor.conn
cloud.mongo.db = DatabaseAgent.accessor.db
})

/**
* Create a new Cloud SDK instance
Expand Down Expand Up @@ -161,14 +161,14 @@ export function create() {
OSS_REGION: process.env.OSS_REGION,
OSS_INTERNAL_ENDPOINT: process.env.OSS_INTERNAL_ENDPOINT,
OSS_EXTERNAL_ENDPOINT: process.env.OSS_EXTERNAL_ENDPOINT,
NPM_INSTALL_FLAGS: process.env.NPM_INSTALL_FLAGS || "",
NPM_INSTALL_FLAGS: process.env.NPM_INSTALL_FLAGS || '',
RUNTIME_IMAGE: process.env.RUNTIME_IMAGE,
},
};
return cloud;
}
return cloud
}

export default cloud;
export default cloud

/**
* The cloud function is invoked in the cloud function, which runs in the cloud function.
Expand All @@ -178,24 +178,25 @@ export default cloud;
* @returns
*/
async function invokeInFunction(name: string, param?: FunctionContext) {
const data = await CloudFunction.getFunctionByName(name);
const func = new CloudFunction(data);
const data = await CloudFunction.getFunctionByName(name)
const func = new CloudFunction(data)

if (!func) {
throw new Error(`invoke() failed to get function: ${name}`);
throw new Error(`invoke() failed to get function: ${name}`)
}

param = param ?? {};
param = param ?? {} as any
param.__function_name = name

param.requestId = param.requestId ?? "invoke";
param.requestId = param.requestId ?? 'invoke'

param.method = param.method ?? "call";
param.method = param.method ?? 'call'

const result = await func.invoke(param);
const result = await func.invoke(param)

if (result.error) {
throw result.error;
throw result.error
}

return result.data;
return result.data
}
7 changes: 4 additions & 3 deletions runtimes/nodejs/src/handler/db-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
* @LastEditTime: 2022-02-03 00:39:18
* @Description:
*/
import { Request, Response } from 'express'
import { Response } from 'express'
import { Proxy } from 'database-proxy'
import Config from '../config'
import { DatabaseAgent } from '../db'
import { logger } from '../support/logger'
import { PolicyAgent } from '../support/policy'
import { IRequest } from '../support/types'


export async function handleDatabaseProxy(req: Request, res: Response) {
export async function handleDatabaseProxy(req: IRequest, res: Response) {
const accessor = DatabaseAgent.accessor

const requestId = req['requestId']
const auth = req['auth'] ?? {}
const auth = req.user || {}
const policy_name = req.params?.policy

// get corresponding policy
Expand Down
13 changes: 8 additions & 5 deletions runtimes/nodejs/src/handler/debug-func.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
* @Description:
*/

import { Request, Response } from 'express'
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'

/**
* Handler of debugging cloud function
*/
export async function handleDebugFunction(req: Request, res: Response) {
export async function handleDebugFunction(req: IRequest, res: Response) {

const requestId = req['requestId']
const func_name = req.params?.name
Expand All @@ -25,7 +26,7 @@ export async function handleDebugFunction(req: Request, res: Response) {
}

// verify the debug token
const auth = req['auth']
const auth = req.user
if (!auth || auth.type !== 'debug') {
return res.status(403).send('permission denied: invalid debug token')
}
Expand All @@ -40,9 +41,11 @@ export async function handleDebugFunction(req: Request, res: Response) {
body: param,
headers: req.headers,
method: req.method,
auth: req['auth'],
auth: req.user,
user: req.user,
requestId,
response: res
response: res,
__function_name: func.name,
}
const result = await func.invoke(ctx)

Expand Down
57 changes: 30 additions & 27 deletions runtimes/nodejs/src/handler/invoke-func.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,40 @@
* @Description:
*/

import { Request, Response } from "express";
import { FunctionContext } from "../support/function-engine";
import { logger } from "../support/logger";
import { CloudFunction } from "../support/function-engine";
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'

const DEFAULT_FUNCTION_NAME = "__default__";
const DEFAULT_FUNCTION_NAME = '__default__'

/**
* Handler of invoking cloud function
*/
export async function handleInvokeFunction(req: Request, res: Response) {
const requestId = req["requestId"];
const func_name = req.params?.name;
export async function handleInvokeFunction(req: IRequest, res: Response) {
const requestId = req['requestId']
const func_name = req.params?.name

// load function data from db
let funcData = await CloudFunction.getFunctionByName(func_name);
let funcData = await CloudFunction.getFunctionByName(func_name)
if (!funcData) {
if (func_name === "healthz") {
return res.status(200).send("ok");
if (func_name === 'healthz') {
return res.status(200).send('ok')
}

// load default function from db
funcData = await CloudFunction.getFunctionByName(DEFAULT_FUNCTION_NAME);
funcData = await CloudFunction.getFunctionByName(DEFAULT_FUNCTION_NAME)
if (!funcData) {
return res.status(404).send("Function Not Found");
return res.status(404).send('Function Not Found')
}
}

const func = new CloudFunction(funcData);
const func = new CloudFunction(funcData)

// reject while no HTTP enabled
if (!func.methods.includes(req.method.toUpperCase())) {
return res.status(405).send("Method Not Allowed");
return res.status(405).send('Method Not Allowed')
}

try {
Expand All @@ -48,38 +49,40 @@ export async function handleInvokeFunction(req: Request, res: Response) {
body: req.body,
headers: req.headers,
method: req.method,
auth: req["auth"],
auth: req['auth'],
user: req.user,
requestId,
request: req,
response: res,
};
const result = await func.invoke(ctx);
__function_name: func.name,
}
const result = await func.invoke(ctx)

if (result.error) {
logger.error(
requestId,
`invoke function ${func_name} invoke error: `,
result
);
result,
)

return res.status(400).send({
error:
"invoke cloud function got error, please check the function logs",
'invoke cloud function got error, please check the function logs',
requestId,
});
})
}

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

if (res.writableEnded === false) {
return res.send(result.data);
return res.send(result.data)
}
} catch (error) {
logger.error(requestId, "failed to invoke error", error);
return res.status(500).send("Internal Server Error");
logger.error(requestId, 'failed to invoke error', error)
return res.status(500).send('Internal Server Error')
}
}
Loading