From da58bf647f23077fc4142f6454cd6c40d8a82e96 Mon Sep 17 00:00:00 2001 From: Harry Chen Date: Wed, 1 Sep 2021 18:33:56 +0800 Subject: [PATCH] feat: add ctx.throw for serverless app (#1262) --- .../faas-typings/typings/common.ts | 22 ++++++++++++++++ .../serverless-http-parser/package.json | 1 + .../serverless-http-parser/src/context.ts | 26 +++++++++++++++++++ .../serverless-http-parser/test/index.test.ts | 15 +++++++++++ packages/oss/jest.setup.js | 2 +- packages/oss/test/sts_client.ts | 14 +++++++++- 6 files changed, 78 insertions(+), 2 deletions(-) diff --git a/packages-serverless/faas-typings/typings/common.ts b/packages-serverless/faas-typings/typings/common.ts index e19ddc39edde..ad2a533f2714 100644 --- a/packages-serverless/faas-typings/typings/common.ts +++ b/packages-serverless/faas-typings/typings/common.ts @@ -432,6 +432,28 @@ export interface FaaSHTTPContext * FaaS Cookies Object */ cookies: Cookies; + + /** + * Throw an error with `msg` and optional `status` + * defaulting to 500. Note that these are user-level + * errors, and the message may be exposed to the client. + * + * this.throw(403) + * this.throw('name required', 400) + * this.throw(400, 'name required') + * this.throw('something exploded') + * this.throw(new Error('invalid'), 400); + * this.throw(400, new Error('invalid')); + * + * See: https://github.com/jshttp/http-errors + */ + throw( + message: string, + code?: number, + properties?: Record + ): never; + throw(status: number): never; + throw(...properties: Array>): never; } export type FaaSOriginContext = FC.RequestContext | SCF.RequestContext; diff --git a/packages-serverless/serverless-http-parser/package.json b/packages-serverless/serverless-http-parser/package.json index ea9407734071..6e7f35bd0aa7 100644 --- a/packages-serverless/serverless-http-parser/package.json +++ b/packages-serverless/serverless-http-parser/package.json @@ -10,6 +10,7 @@ "cookies": "^0.8.0", "encodeurl": "^1.0.2", "escape-html": "^1.0.3", + "http-errors": "^1.6.3", "only": "^0.0.2", "parseurl": "^1.3.3", "querystring": "^0.2.0", diff --git a/packages-serverless/serverless-http-parser/src/context.ts b/packages-serverless/serverless-http-parser/src/context.ts index 82b623713d24..46b3e5d81ae3 100644 --- a/packages-serverless/serverless-http-parser/src/context.ts +++ b/packages-serverless/serverless-http-parser/src/context.ts @@ -1,5 +1,6 @@ import { FaaSOriginContext } from '@midwayjs/faas-typings'; import * as util from 'util'; +import * as createError from 'http-errors'; const Cookies = require('cookies'); const COOKIES = Symbol('context#cookies'); @@ -156,6 +157,31 @@ export const context = { return this.request.acceptsLanguages(...args); }, + /** + * Throw an error with `status` (default 500) and + * `msg`. Note that these are user-level + * errors, and the message may be exposed to the client. + * + * this.throw(403) + * this.throw(400, 'name required') + * this.throw('something exploded') + * this.throw(new Error('invalid')) + * this.throw(400, new Error('invalid')) + * + * See: https://github.com/jshttp/http-errors + * + * Note: `status` should only be passed as the first parameter. + * + * @param {String|Number|Error} err, msg or status + * @param {String|Number|Error} [err, msg or status] + * @param {Object} [props] + * @api public + */ + + throw(...args) { + throw createError(...args); + }, + onerror(err) { // don't do anything if there is no error. // this allows you to pass `this.onerror` diff --git a/packages-serverless/serverless-http-parser/test/index.test.ts b/packages-serverless/serverless-http-parser/test/index.test.ts index 3192438d7b3e..1b0d6f4ac96c 100644 --- a/packages-serverless/serverless-http-parser/test/index.test.ts +++ b/packages-serverless/serverless-http-parser/test/index.test.ts @@ -607,6 +607,21 @@ describe('test http parser', () => { app.onerror(err); assert(msg === ' Error: mock stack null'); }); + it('should test ctx.throw', function () { + const app = new Application(); + const req = new HTTPRequest( + require('./resource/scf_apigw.json'), + require('./resource/scf_ctx.json') + ); + const res = new HTTPResponse(); + const ctx: FaaSHTTPContext = app.createContext(req, res); + try { + ctx.throw(403, 'throw error'); + } catch (er) { + // got err + } + assert('run here'); + }); }); }); diff --git a/packages/oss/jest.setup.js b/packages/oss/jest.setup.js index d8e9399e3e3b..60ae75cb7e17 100644 --- a/packages/oss/jest.setup.js +++ b/packages/oss/jest.setup.js @@ -2,7 +2,7 @@ const path = require('path'); const fs = require('fs'); process.env.MIDWAY_TS_MODE = 'true'; -jest.setTimeout(30000); +jest.setTimeout(60000); const envFile = path.join(__dirname, '.env'); if (!fs.existsSync(envFile)) { diff --git a/packages/oss/test/sts_client.ts b/packages/oss/test/sts_client.ts index 2705d12a9020..64f423d687d3 100644 --- a/packages/oss/test/sts_client.ts +++ b/packages/oss/test/sts_client.ts @@ -1,6 +1,6 @@ const OSS = require('ali-oss'); -export function createSTSClient(accessKeyId, accessKeySecret, stsToken) { +function createClient(accessKeyId, accessKeySecret, stsToken) { return new OSS({ accessKeyId, accessKeySecret, @@ -11,3 +11,15 @@ export function createSTSClient(accessKeyId, accessKeySecret, stsToken) { secure: true, }); } + +export function createSTSClient(accessKeyId, accessKeySecret, stsToken) { + let client; + try { + client = createClient(accessKeyId, accessKeySecret, stsToken); + } catch (err) { + console.warn('create sts client error and retry'); + client = createClient(accessKeyId, accessKeySecret, stsToken); + } + + return client; +}