diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 6b576b5..634d0f2 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,47 +1,23 @@ -name: Node.js CI +name: CI on: push: - branches: - - main - - master - pull_request: - branches: - - main - - master - schedule: - - cron: '0 2 * * *' - -jobs: - build: - runs-on: ${{ matrix.os }} - - strategy: - fail-fast: false - matrix: - node-version: [14, 16, 18] - os: [ubuntu-latest] + branches: [ master ] - steps: - - name: Checkout Git Source - uses: actions/checkout@v2 + pull_request: + branches: [ master ] - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} + workflow_dispatch: {} - - name: Install Dependencies - run: | +jobs: + Job: + name: Node.js + uses: artusjs/github-actions/.github/workflows/node-test.yml@v1 + with: + os: 'ubuntu-latest' + version: '14, 16, 18' + install: | tar xf zookeeper-3.4.6.tar.gz mv zookeeper-3.4.6/conf/zoo_sample.cfg zookeeper-3.4.6/conf/zoo.cfg ./zookeeper-3.4.6/bin/zkServer.sh start - npm i - - - name: Continuous Integration - run: npm run ci - - - name: Code Coverage - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} + npm i --no-package-lock --no-fund diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1612587 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,17 @@ +name: Release + +on: + push: + branches: [ master ] + + workflow_dispatch: {} + +jobs: + release: + name: Node.js + uses: artusjs/github-actions/.github/workflows/node-release.yml@v1 + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GIT_TOKEN: ${{ secrets.GIT_TOKEN }} + with: + checkTest: false diff --git a/lib/client/consumer.js b/lib/client/consumer.js index b27dc7b..88a6373 100644 --- a/lib/client/consumer.js +++ b/lib/client/consumer.js @@ -190,9 +190,9 @@ class RpcConsumer extends Base { async invoke(method, args, options = {}) { if (!this._isReady) { try { - await this.readyOrTimeout(options.responseTimeout || this.options.responseTimeout) + await this.readyOrTimeout(options.responseTimeout || this.options.responseTimeout); } catch (err) { - throw new Error('[RpcConsumer] Consumer ready error: ' + err.message) + throw new Error('[RpcConsumer] Consumer ready error: ' + err.message); } } const req = this.createRequest(method, args, options); diff --git a/lib/server/server.js b/lib/server/server.js index 5d2505d..c85f0c4 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -271,15 +271,25 @@ class RpcServer extends Base { } async _handleRequest(req, conn) { - const id = req.data.serverSignature; + const serviceId = req.data.serverSignature; req.data.interfaceName = req.data.interfaceName || req.data.serverSignature.split(':')[0]; - const service = this._services.get(id); const res = new this.responseClass(req, conn); const ctx = this.createContext(req, res); + if (this.localStorage && ctx) { + await this.localStorage.run(ctx, async () => { + await this._invokeService(serviceId, req, res, ctx); + }); + } else { + await this._invokeService(serviceId, req, res, ctx); + } + } + + async _invokeService(serviceId, req, res, ctx) { + const service = this._services.get(serviceId); this.emit('request', { req, ctx }); try { if (!service) { - throw new Error('not found service: ' + id); + throw new Error('not found service: ' + serviceId); } await service.invoke(ctx, req, res); } catch (err) { diff --git a/package.json b/package.json index 09cd24f..2f9ca02 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,7 @@ "cov": "egg-bin cov", "test": "npm run lint && npm run test-local", "test-local": "egg-bin test", - "pkgfiles": "egg-bin pkgfiles --check", - "ci": "npm run start:zk && npm run pkgfiles && npm run lint && npm run cov", + "ci": "npm run start:zk && npm run lint && npm run cov", "contributors": "contributors -f plain -o AUTHORS", "start:zk": "node test/scripts/start.js", "stop:zk": "node test/scripts/stop.js" @@ -45,7 +44,7 @@ "koa-compose": "^4.1.0", "mz-modules": "^2.1.0", "pump": "^3.0.0", - "sdk-base": "^4.0.0", + "sdk-base": "^4.2.1", "sofa-bolt-node": "^2.0.1", "urlencode": "^1.1.0", "utility": "^1.16.3", diff --git a/test/client/consumer.test.js b/test/client/consumer.test.js index 5b04aad..e8fc62a 100644 --- a/test/client/consumer.test.js +++ b/test/client/consumer.test.js @@ -516,20 +516,20 @@ describe('test/client/consumer.test.js', () => { }, registry, logger, - responseTimeout: 10 + responseTimeout: 10, }); - await consumer.ready() - consumer._isReady = false - consumer.ready(false) + await consumer.ready(); + consumer._isReady = false; + consumer.ready(false); try { - await consumer.invoke('test', [{}]) + await consumer.invoke('test', [{}]); assert(false); } catch (err) { assert(err && err.message.includes('[RpcConsumer] Consumer ready error: Promise timed out after 10 milliseconds')); } - }) + }); describe('should filter invalid address', () => { class CustomRegistry extends Base { diff --git a/test/server/server.test.js b/test/server/server.test.js index 040718c..b05455b 100644 --- a/test/server/server.test.js +++ b/test/server/server.test.js @@ -1,14 +1,13 @@ -'use strict'; - -const mm = require('mm'); const net = require('net'); +const { AsyncLocalStorage } = require('async_hooks'); const assert = require('assert'); +const mm = require('mm'); const sleep = require('mz-modules/sleep'); -const request = require('../../').test; const dubboProtocol = require('dubbo-remoting'); +const protocol = require('sofa-bolt-node/lib/protocol'); +const request = require('../../').test; const RpcClient = require('../../').client.RpcClient; const RpcServer = require('../../').server.RpcServer; -const protocol = require('sofa-bolt-node/lib/protocol'); const ZookeeperRegistry = require('../../').registry.ZookeeperRegistry; const logger = console; @@ -394,4 +393,92 @@ describe('test/server/server.test.js', () => { }); }); + describe('bolt with localStorage', () => { + const asyncLocalStorage = new AsyncLocalStorage(); + before(async () => { + class NewRpcServer extends RpcServer { + createContext(req) { + return { req }; + } + } + server = new NewRpcServer({ + appName: 'test', + registry, + version, + logger, + port: 0, + localStorage: asyncLocalStorage, + }); + server.addService({ + interfaceName: 'com.alipay.x.facade.HelloRpcFacade', + version, + apiMeta: { + methods: [{ + name: 'plus', + parameterTypes: [ + 'java.lang.Integer', + 'java.lang.Integer', + ], + returnType: 'java.lang.Integer', + }], + }, + }, { + // a + b + async plus(a, b) { + return a + b; + }, + }); + server.addService({ + interfaceName: 'com.alipay.test.TestService', + }, { + async error() { + const ctx = asyncLocalStorage.getStore(); + console.log('ctx', !!ctx, typeof ctx); + throw new Error('mock error with ctx ' + !!ctx); + }, + }); + server.addService({ + interfaceName: 'com.alipay.test.HelloService', + version, + uniqueId: 'hello', + }, { + async hello() { + await sleep(2000); + return 'hello'; + }, + }); + await server.start(); + await server.publish(); + }); + + after(async () => { + await server.close(); + server = null; + }); + + it('should invoke ok', () => { + return request(server) + .service('com.alipay.x.facade.HelloRpcFacade') + .invoke('plus') + .send([ 1, 2 ]) + .expect(3); + }); + + it('should resultCode=01 if biz error', async () => { + let meta; + server.once('response', data => { + meta = data.res.meta; + }); + + await request(server) + .service('com.alipay.test.TestService') + .invoke('error') + .timeout(1000) + .send([]) + .error(/mock error with ctx true/); + + assert(meta && meta.resultCode === '01'); + }); + }); + });