From c4a7605c3674ae23956dabf89250b257ed74fc09 Mon Sep 17 00:00:00 2001 From: Suyi Date: Tue, 26 Mar 2024 00:18:30 +0800 Subject: [PATCH] feat(@artusx/plugin-grpc): add grpc support --- common/config/rush/pnpm-lock.yaml | 184 ++++++++++++++++++ packages/apps/artusx-grpc/.eslintignore | 4 + packages/apps/artusx-grpc/.eslintrc | 6 + packages/apps/artusx-grpc/.gitignore | 1 + packages/apps/artusx-grpc/.npmrc | 3 + packages/apps/artusx-grpc/README.md | 1 + packages/apps/artusx-grpc/package.json | 37 ++++ packages/apps/artusx-grpc/src/bootstrap.ts | 11 ++ .../artusx-grpc/src/config/config.default.ts | 27 +++ .../src/config/config.development.ts | 1 + .../src/config/config.production.ts | 1 + .../apps/artusx-grpc/src/config/plugin.ts | 6 + packages/apps/artusx-grpc/src/index.ts | 3 + .../apps/artusx-grpc/src/protos/echo.proto | 45 +++++ .../artusx-grpc/src/protos/helloworld.proto | 40 ++++ packages/apps/artusx-grpc/tsconfig.json | 7 + packages/libs/utils/src/constants.ts | 2 + packages/plugins/grpc/.eslintignore | 4 + packages/plugins/grpc/.eslintrc | 6 + packages/plugins/grpc/.gitignore | 1 + packages/plugins/grpc/.npmrc | 3 + packages/plugins/grpc/README.md | 1 + packages/plugins/grpc/jest.config.js | 6 + packages/plugins/grpc/package.json | 66 +++++++ packages/plugins/grpc/src/client.ts | 124 ++++++++++++ .../plugins/grpc/src/config/config.default.ts | 3 + packages/plugins/grpc/src/constants.ts | 3 + packages/plugins/grpc/src/index.ts | 2 + packages/plugins/grpc/src/lifecycle.ts | 36 ++++ packages/plugins/grpc/src/meta.json | 3 + packages/plugins/grpc/tsconfig.build.json | 4 + packages/plugins/grpc/tsconfig.json | 9 + rush.json | 12 ++ 33 files changed, 662 insertions(+) create mode 100644 packages/apps/artusx-grpc/.eslintignore create mode 100644 packages/apps/artusx-grpc/.eslintrc create mode 100644 packages/apps/artusx-grpc/.gitignore create mode 100644 packages/apps/artusx-grpc/.npmrc create mode 100644 packages/apps/artusx-grpc/README.md create mode 100644 packages/apps/artusx-grpc/package.json create mode 100644 packages/apps/artusx-grpc/src/bootstrap.ts create mode 100644 packages/apps/artusx-grpc/src/config/config.default.ts create mode 100644 packages/apps/artusx-grpc/src/config/config.development.ts create mode 100644 packages/apps/artusx-grpc/src/config/config.production.ts create mode 100644 packages/apps/artusx-grpc/src/config/plugin.ts create mode 100644 packages/apps/artusx-grpc/src/index.ts create mode 100644 packages/apps/artusx-grpc/src/protos/echo.proto create mode 100644 packages/apps/artusx-grpc/src/protos/helloworld.proto create mode 100644 packages/apps/artusx-grpc/tsconfig.json create mode 100644 packages/plugins/grpc/.eslintignore create mode 100644 packages/plugins/grpc/.eslintrc create mode 100644 packages/plugins/grpc/.gitignore create mode 100644 packages/plugins/grpc/.npmrc create mode 100644 packages/plugins/grpc/README.md create mode 100644 packages/plugins/grpc/jest.config.js create mode 100644 packages/plugins/grpc/package.json create mode 100644 packages/plugins/grpc/src/client.ts create mode 100644 packages/plugins/grpc/src/config/config.default.ts create mode 100644 packages/plugins/grpc/src/constants.ts create mode 100644 packages/plugins/grpc/src/index.ts create mode 100644 packages/plugins/grpc/src/lifecycle.ts create mode 100644 packages/plugins/grpc/src/meta.json create mode 100644 packages/plugins/grpc/tsconfig.build.json create mode 100644 packages/plugins/grpc/tsconfig.json diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index da3c1d52..9c7ff035 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -42,6 +42,49 @@ importers: specifier: ~5.3.3 version: 5.3.3 + ../../packages/apps/artusx-grpc: + dependencies: + '@artusx/plugin-grpc': + specifier: workspace:* + version: link:../../plugins/grpc + '@artusx/utils': + specifier: workspace:* + version: link:../../libs/utils + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + tslib: + specifier: ^2.5.0 + version: 2.6.2 + devDependencies: + '@artusx/eslint-config': + specifier: workspace:* + version: link:../../../toolchains/eslint-config + '@artusx/tsconfig': + specifier: workspace:* + version: link:../../../toolchains/tsconfig + '@types/node': + specifier: ^18.11.17 + version: 18.19.24 + '@typescript-eslint/eslint-plugin': + specifier: ~6.19.1 + version: 6.19.1(eslint@8.56.0)(typescript@4.9.5) + eslint: + specifier: ~8.56.0 + version: 8.56.0 + eslint-plugin-import: + specifier: ~2.29.1 + version: 2.29.1(eslint@8.56.0) + nodemon: + specifier: ~3.0.2 + version: 3.0.3 + ts-node: + specifier: ^10.9.1 + version: 10.9.2(@types/node@18.19.24)(typescript@4.9.5) + typescript: + specifier: ^4.9.4 + version: 4.9.5 + ../../packages/apps/artusx-koa: dependencies: '@artusx/core': @@ -351,6 +394,58 @@ importers: specifier: ^4.9.4 version: 4.9.5 + ../../packages/plugins/grpc: + dependencies: + '@artus/core': + specifier: ^2.x + version: 2.2.2(reflect-metadata@0.2.1) + '@artus/pipeline': + specifier: ^0.2 + version: 0.2.3 + '@grpc/grpc-js': + specifier: ~1.10.3 + version: 1.10.3 + '@grpc/proto-loader': + specifier: ~0.7.10 + version: 0.7.10 + devDependencies: + '@artusx/eslint-config': + specifier: workspace:* + version: link:../../../toolchains/eslint-config + '@artusx/tsconfig': + specifier: workspace:* + version: link:../../../toolchains/tsconfig + '@types/jest': + specifier: ~29.5.11 + version: 29.5.12 + '@types/node': + specifier: ^18.11.17 + version: 18.19.24 + '@typescript-eslint/eslint-plugin': + specifier: ~6.19.1 + version: 6.19.1(eslint@8.56.0)(typescript@4.9.5) + eslint: + specifier: ~8.56.0 + version: 8.56.0 + eslint-plugin-import: + specifier: ~2.29.1 + version: 2.29.1(eslint@8.56.0) + jest: + specifier: ~29.7.0 + version: 29.7.0(@types/node@18.19.24) + reflect-metadata: + specifier: ~0.2.1 + version: 0.2.1 + ts-jest: + specifier: ~29.1.2 + version: 29.1.2(jest@29.7.0)(typescript@4.9.5) + tslib: + specifier: ^2.5.0 + version: 2.6.2 + typescript: + specifier: ^4.9.4 + version: 4.9.5 + ../../packages/plugins/koa: dependencies: '@artus/core': @@ -1446,6 +1541,25 @@ packages: resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} dev: false + /@grpc/grpc-js@1.10.3: + resolution: {integrity: sha512-qiO9MNgYnwbvZ8MK0YLWbnGrNX3zTcj6/Ef7UHu5ZofER3e2nF3Y35GaPo9qNJJ/UJQKa4KL+z/F4Q8Q+uCdUQ==} + engines: {node: '>=12.10.0'} + dependencies: + '@grpc/proto-loader': 0.7.10 + '@js-sdsl/ordered-map': 4.4.2 + dev: false + + /@grpc/proto-loader@0.7.10: + resolution: {integrity: sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + lodash.camelcase: 4.3.0 + long: 5.2.3 + protobufjs: 7.2.6 + yargs: 17.7.2 + dev: false + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -1729,6 +1843,10 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@js-sdsl/ordered-map@4.4.2: + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + dev: false + /@koa/bodyparser@5.0.0: resolution: {integrity: sha512-JEiZVe2e85qPOqA+Nw/SJC5fkFw3XSekh0RSoqz5F6lFYuhEspgqAb972rQRCJesv27QUsz96vU/Vb92wF1GUg==} engines: {node: '>= 16'} @@ -1883,6 +2001,49 @@ packages: - encoding dev: false + /@protobufjs/aspromise@1.1.2: + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + dev: false + + /@protobufjs/base64@1.1.2: + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + dev: false + + /@protobufjs/codegen@2.0.4: + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + dev: false + + /@protobufjs/eventemitter@1.1.0: + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + dev: false + + /@protobufjs/fetch@1.1.0: + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + dev: false + + /@protobufjs/float@1.0.2: + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + dev: false + + /@protobufjs/inquire@1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + dev: false + + /@protobufjs/path@1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + dev: false + + /@protobufjs/pool@1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + dev: false + + /@protobufjs/utf8@1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + dev: false + /@puppeteer/browsers@2.2.0: resolution: {integrity: sha512-MC7LxpcBtdfTbzwARXIkqGZ1Osn3nnZJlm+i0+VqHl72t//Xwl9wICrXT8BwtgC6s1xJNHsxOpvzISUqe92+sw==} engines: {node: '>=18'} @@ -6620,6 +6781,10 @@ packages: dependencies: p-locate: 5.0.0 + /lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + dev: false + /lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} dev: false @@ -7731,6 +7896,25 @@ packages: sisteransi: 1.0.5 dev: true + /protobufjs@7.2.6: + resolution: {integrity: sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==} + engines: {node: '>=12.0.0'} + requiresBuild: true + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 20.11.28 + long: 5.2.3 + dev: false + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} diff --git a/packages/apps/artusx-grpc/.eslintignore b/packages/apps/artusx-grpc/.eslintignore new file mode 100644 index 00000000..42aa3099 --- /dev/null +++ b/packages/apps/artusx-grpc/.eslintignore @@ -0,0 +1,4 @@ +node_modules +lib +dist +coverage \ No newline at end of file diff --git a/packages/apps/artusx-grpc/.eslintrc b/packages/apps/artusx-grpc/.eslintrc new file mode 100644 index 00000000..b29cd197 --- /dev/null +++ b/packages/apps/artusx-grpc/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": ["@artusx/eslint-config"], + "parserOptions": { + "project": "./tsconfig.json" + } +} diff --git a/packages/apps/artusx-grpc/.gitignore b/packages/apps/artusx-grpc/.gitignore new file mode 100644 index 00000000..a65b4177 --- /dev/null +++ b/packages/apps/artusx-grpc/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/apps/artusx-grpc/.npmrc b/packages/apps/artusx-grpc/.npmrc new file mode 100644 index 00000000..862731de --- /dev/null +++ b/packages/apps/artusx-grpc/.npmrc @@ -0,0 +1,3 @@ +registry=https://registry.npmjs.org/ +always-auth=false +strict-ssl=false diff --git a/packages/apps/artusx-grpc/README.md b/packages/apps/artusx-grpc/README.md new file mode 100644 index 00000000..5f7c5a85 --- /dev/null +++ b/packages/apps/artusx-grpc/README.md @@ -0,0 +1 @@ +# artusx-grpc diff --git a/packages/apps/artusx-grpc/package.json b/packages/apps/artusx-grpc/package.json new file mode 100644 index 00000000..528a8dc2 --- /dev/null +++ b/packages/apps/artusx-grpc/package.json @@ -0,0 +1,37 @@ +{ + "name": "artusx-grpc", + "version": "0.0.0-dev.0", + "description": "grpc app powered by artusx", + "keywords": [ + "artusx" + ], + "author": "Suyi ", + "main": "dist/index.js", + "scripts": { + "_build": "npm run tsc", + "build": "", + "ci": "npm run lint", + "dev": "ARTUS_SERVER_ENV=development npx nodemon src/index.ts", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "start": "ARTUS_SERVER_ENV=production node dist/index.js", + "tsc": "rm -rf dist && tsc" + }, + "dependencies": { + "@artusx/plugin-grpc": "workspace:*", + "@artusx/utils": "workspace:*", + "reflect-metadata": "^0.1.13", + "tslib": "^2.5.0" + }, + "devDependencies": { + "@artusx/eslint-config": "workspace:*", + "@artusx/tsconfig": "workspace:*", + "@types/node": "^18.11.17", + "@typescript-eslint/eslint-plugin": "~6.19.1", + "eslint": "~8.56.0", + "eslint-plugin-import": "~2.29.1", + "nodemon": "~3.0.2", + "ts-node": "^10.9.1", + "typescript": "^4.9.4" + } +} diff --git a/packages/apps/artusx-grpc/src/bootstrap.ts b/packages/apps/artusx-grpc/src/bootstrap.ts new file mode 100644 index 00000000..8e8566b7 --- /dev/null +++ b/packages/apps/artusx-grpc/src/bootstrap.ts @@ -0,0 +1,11 @@ +import path from 'path'; +import { Application } from '@artusx/utils'; + +export const main = async () => { + const app = await Application.start({ + root: path.resolve(__dirname), + configDir: 'config', + }); + + return app; +}; diff --git a/packages/apps/artusx-grpc/src/config/config.default.ts b/packages/apps/artusx-grpc/src/config/config.default.ts new file mode 100644 index 00000000..30917393 --- /dev/null +++ b/packages/apps/artusx-grpc/src/config/config.default.ts @@ -0,0 +1,27 @@ +import path from 'path'; + +export default () => { + const grpc = { + protoList: [ + { + protoName: 'helloworld', + protoPath: path.resolve(__dirname, '../protos/helloworld.proto'), + serviceMap: { + Greeter: { + sayHello: async (call, callback) => { + callback(null, { message: 'Hello ' + call.request.name }); + }, + }, + }, + }, + ], + server: { + host: '0.0.0.0', + port: '50051', + }, + }; + + return { + grpc, + }; +}; diff --git a/packages/apps/artusx-grpc/src/config/config.development.ts b/packages/apps/artusx-grpc/src/config/config.development.ts new file mode 100644 index 00000000..ff8b4c56 --- /dev/null +++ b/packages/apps/artusx-grpc/src/config/config.development.ts @@ -0,0 +1 @@ +export default {}; diff --git a/packages/apps/artusx-grpc/src/config/config.production.ts b/packages/apps/artusx-grpc/src/config/config.production.ts new file mode 100644 index 00000000..ff8b4c56 --- /dev/null +++ b/packages/apps/artusx-grpc/src/config/config.production.ts @@ -0,0 +1 @@ +export default {}; diff --git a/packages/apps/artusx-grpc/src/config/plugin.ts b/packages/apps/artusx-grpc/src/config/plugin.ts new file mode 100644 index 00000000..56f14a8e --- /dev/null +++ b/packages/apps/artusx-grpc/src/config/plugin.ts @@ -0,0 +1,6 @@ +export default { + grpc: { + enable: true, + package: '@artusx/plugin-grpc', + }, +}; diff --git a/packages/apps/artusx-grpc/src/index.ts b/packages/apps/artusx-grpc/src/index.ts new file mode 100644 index 00000000..dcbdcdc2 --- /dev/null +++ b/packages/apps/artusx-grpc/src/index.ts @@ -0,0 +1,3 @@ +import { main } from './bootstrap'; + +main(); diff --git a/packages/apps/artusx-grpc/src/protos/echo.proto b/packages/apps/artusx-grpc/src/protos/echo.proto new file mode 100644 index 00000000..4814217d --- /dev/null +++ b/packages/apps/artusx-grpc/src/protos/echo.proto @@ -0,0 +1,45 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + syntax = "proto3"; + + option go_package = "google.golang.org/grpc/examples/features/proto/echo"; + + package grpc.examples.echo; + + // EchoRequest is the request for echo. + message EchoRequest { + string message = 1; + } + + // EchoResponse is the response for echo. + message EchoResponse { + string message = 1; + } + + // Echo is the echo service. + service Echo { + // UnaryEcho is unary echo. + rpc UnaryEcho(EchoRequest) returns (EchoResponse) {} + // ServerStreamingEcho is server side streaming. + rpc ServerStreamingEcho(EchoRequest) returns (stream EchoResponse) {} + // ClientStreamingEcho is client side streaming. + rpc ClientStreamingEcho(stream EchoRequest) returns (EchoResponse) {} + // BidirectionalStreamingEcho is bidi streaming. + rpc BidirectionalStreamingEcho(stream EchoRequest) returns (stream EchoResponse) {} + } \ No newline at end of file diff --git a/packages/apps/artusx-grpc/src/protos/helloworld.proto b/packages/apps/artusx-grpc/src/protos/helloworld.proto new file mode 100644 index 00000000..7e50d0fc --- /dev/null +++ b/packages/apps/artusx-grpc/src/protos/helloworld.proto @@ -0,0 +1,40 @@ +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; +option objc_class_prefix = "HLW"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} + + rpc SayHelloStreamReply (HelloRequest) returns (stream HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/packages/apps/artusx-grpc/tsconfig.json b/packages/apps/artusx-grpc/tsconfig.json new file mode 100644 index 00000000..aa99379c --- /dev/null +++ b/packages/apps/artusx-grpc/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@artusx/tsconfig", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/libs/utils/src/constants.ts b/packages/libs/utils/src/constants.ts index bb1e615b..e89f93ae 100644 --- a/packages/libs/utils/src/constants.ts +++ b/packages/libs/utils/src/constants.ts @@ -18,6 +18,8 @@ export enum ArtusXInjectEnum { Proxy = 'ARTUSX_PROXY', OpenAI = 'ARTUS_OPENAI', Telegram = 'ARTUS_TELEGRAM', + + GRPC = 'ARTUSX_GRPC', } export enum ArtusXErrorEnum { diff --git a/packages/plugins/grpc/.eslintignore b/packages/plugins/grpc/.eslintignore new file mode 100644 index 00000000..42aa3099 --- /dev/null +++ b/packages/plugins/grpc/.eslintignore @@ -0,0 +1,4 @@ +node_modules +lib +dist +coverage \ No newline at end of file diff --git a/packages/plugins/grpc/.eslintrc b/packages/plugins/grpc/.eslintrc new file mode 100644 index 00000000..b29cd197 --- /dev/null +++ b/packages/plugins/grpc/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": ["@artusx/eslint-config"], + "parserOptions": { + "project": "./tsconfig.json" + } +} diff --git a/packages/plugins/grpc/.gitignore b/packages/plugins/grpc/.gitignore new file mode 100644 index 00000000..a65b4177 --- /dev/null +++ b/packages/plugins/grpc/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/plugins/grpc/.npmrc b/packages/plugins/grpc/.npmrc new file mode 100644 index 00000000..862731de --- /dev/null +++ b/packages/plugins/grpc/.npmrc @@ -0,0 +1,3 @@ +registry=https://registry.npmjs.org/ +always-auth=false +strict-ssl=false diff --git a/packages/plugins/grpc/README.md b/packages/plugins/grpc/README.md new file mode 100644 index 00000000..cad1f7ba --- /dev/null +++ b/packages/plugins/grpc/README.md @@ -0,0 +1 @@ +# @artusx/plugin-grpc diff --git a/packages/plugins/grpc/jest.config.js b/packages/plugins/grpc/jest.config.js new file mode 100644 index 00000000..2361426b --- /dev/null +++ b/packages/plugins/grpc/jest.config.js @@ -0,0 +1,6 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + collectCoverageFrom: ['/src/**/*.ts'], +}; diff --git a/packages/plugins/grpc/package.json b/packages/plugins/grpc/package.json new file mode 100644 index 00000000..4f8a5c87 --- /dev/null +++ b/packages/plugins/grpc/package.json @@ -0,0 +1,66 @@ +{ + "name": "@artusx/plugin-grpc", + "version": "0.0.0-dev.0", + "description": "grpc plugin for artusx", + "keywords": [ + "artusx" + ], + "author": "Suyi ", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./constants": { + "types": "./lib/constants.d.ts", + "default": "./lib/constants.js" + }, + "./client": { + "types": "./lib/client.d.ts", + "default": "./lib/client.js" + }, + "./lifecycle": { + "types": "./lib/lifecycle.d.ts", + "default": "./lib/lifecycle.js" + } + }, + "main": "lib/index.js", + "types": "lib/index.d.js", + "files": [ + "lib" + ], + "scripts": { + "build": "npm run tsc", + "cov": "jest --coverage --detectOpenHandles --testTimeout=15000", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "test": "jest --detectOpenHandles --testTimeout=15000", + "tsc": "rm -rf lib && tsc -p ./tsconfig.build.json" + }, + "dependencies": { + "@artus/core": "^2.x", + "@artus/pipeline": "^0.2", + "@grpc/grpc-js": "~1.10.3", + "@grpc/proto-loader": "~0.7.10" + }, + "devDependencies": { + "@artusx/eslint-config": "workspace:*", + "@artusx/tsconfig": "workspace:*", + "@types/jest": "~29.5.11", + "@types/node": "^18.11.17", + "@typescript-eslint/eslint-plugin": "~6.19.1", + "eslint": "~8.56.0", + "eslint-plugin-import": "~2.29.1", + "jest": "~29.7.0", + "reflect-metadata": "~0.2.1", + "ts-jest": "~29.1.2", + "tslib": "^2.5.0", + "typescript": "^4.9.4" + }, + "peerDependencies": { + "reflect-metadata": "^0.1.13" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/grpc/src/client.ts b/packages/plugins/grpc/src/client.ts new file mode 100644 index 00000000..c9ccadb6 --- /dev/null +++ b/packages/plugins/grpc/src/client.ts @@ -0,0 +1,124 @@ +import assert from 'assert'; +import { Injectable, ScopeEnum } from '@artus/core'; +import { ArtusXInjectEnum } from './constants'; +import * as grpc from '@grpc/grpc-js'; +import * as protoLoader from '@grpc/proto-loader'; + +import type { Server, GrpcObject, UntypedServiceImplementation } from '@grpc/grpc-js'; +import type { PackageDefinition } from '@grpc/proto-loader'; + +export interface ProtoConfig { + protoName: string; + protoPath: string; + serviceMap: { + [key: string]: UntypedServiceImplementation; + }; +} + +export interface GRPCConfig { + protoList: ProtoConfig[]; + server?: { + host: string; + port: number; + }; + client?: { + host: string; + port: number; + }; +} + +@Injectable({ + id: ArtusXInjectEnum.GRPC, + scope: ScopeEnum.SINGLETON, +}) +export default class GRPCClient { + private config: GRPCConfig; + private client: any; + private server: Server; + + private packageObject: GrpcObject; + private packageDefinition: PackageDefinition; + + async init(config: GRPCConfig) { + if (!config) { + return; + } + + const { protoList } = config; + const protoPathList = protoList.map((item) => item.protoPath); + + const packageDefinition = protoLoader.loadSync(protoPathList, { + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true, + }); + + const packageObject = grpc.loadPackageDefinition(packageDefinition); + + this.config = config; + this.packageObject = packageObject; + this.packageDefinition = packageDefinition; + } + + getPackageObject() { + return this.packageObject; + } + + getPackageDefinition() { + return this.packageDefinition; + } + + async initClient() {} + + getClient() { + return this.client; + } + + async initServer() { + assert(this.config?.server, 'server config is required'); + + const { host = '0.0.0.0', port = '50051' } = this.config?.server; + + const server = new grpc.Server(); + const serverUrl = `${host}:${port}`; + + const protoList = this.config?.protoList; + const packageObject = this.packageObject; + + protoList.forEach((item) => { + const { serviceMap, protoName } = item; + + const grpcObject = packageObject[protoName]; + console.log('grpcObject', grpcObject); + + for (const serviceName in serviceMap) { + console.log('serviceName', serviceName); + + const service = grpcObject[serviceName]; + console.log('service', service); + + const handler = serviceMap[serviceName]; + console.log('handler', handler); + + server.addService(service.service, handler); + } + }); + + server.bindAsync(serverUrl, grpc.ServerCredentials.createInsecure(), (err, port) => { + if (err != null) { + return console.error(err); + } + console.log(`gRPC listening on ${port}`); + }); + + this.server = server; + } + + getServer() { + return this.server; + } +} + +export { GRPCClient }; diff --git a/packages/plugins/grpc/src/config/config.default.ts b/packages/plugins/grpc/src/config/config.default.ts new file mode 100644 index 00000000..a8189f38 --- /dev/null +++ b/packages/plugins/grpc/src/config/config.default.ts @@ -0,0 +1,3 @@ +export default { + protoPath: 'protos', +}; diff --git a/packages/plugins/grpc/src/constants.ts b/packages/plugins/grpc/src/constants.ts new file mode 100644 index 00000000..85ec705f --- /dev/null +++ b/packages/plugins/grpc/src/constants.ts @@ -0,0 +1,3 @@ +export enum ArtusXInjectEnum { + GRPC = 'ARTUSX_GRPC', +} diff --git a/packages/plugins/grpc/src/index.ts b/packages/plugins/grpc/src/index.ts new file mode 100644 index 00000000..48ab86b5 --- /dev/null +++ b/packages/plugins/grpc/src/index.ts @@ -0,0 +1,2 @@ +export * from './client'; +export * from './lifecycle'; diff --git a/packages/plugins/grpc/src/lifecycle.ts b/packages/plugins/grpc/src/lifecycle.ts new file mode 100644 index 00000000..f63d5433 --- /dev/null +++ b/packages/plugins/grpc/src/lifecycle.ts @@ -0,0 +1,36 @@ +import assert from 'assert'; +import { + ApplicationLifecycle, + ArtusApplication, + Inject, + ArtusInjectEnum, + LifecycleHookUnit, + LifecycleHook, +} from '@artus/core'; +import { ArtusXInjectEnum } from './constants'; +import GRPCClient, { GRPCConfig } from './client'; + +@LifecycleHookUnit() +export default class GRPCLifecycle implements ApplicationLifecycle { + @Inject(ArtusInjectEnum.Application) + app: ArtusApplication; + + @LifecycleHook() + async willReady() { + const config: GRPCConfig = this.app.config.grpc; + + assert(config, 'grpc config is required'); + assert(config.protoList, 'grpc config.protoList is required'); + + const client = this.app.container.get(ArtusXInjectEnum.GRPC) as GRPCClient; + await client.init(config); + + if (config.server) { + await client.initServer(); + } + + if (config.client) { + await client.initClient(); + } + } +} diff --git a/packages/plugins/grpc/src/meta.json b/packages/plugins/grpc/src/meta.json new file mode 100644 index 00000000..65f9815a --- /dev/null +++ b/packages/plugins/grpc/src/meta.json @@ -0,0 +1,3 @@ +{ + "name": "grpc" +} diff --git a/packages/plugins/grpc/tsconfig.build.json b/packages/plugins/grpc/tsconfig.build.json new file mode 100644 index 00000000..57934f70 --- /dev/null +++ b/packages/plugins/grpc/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts", "src/**/*.json"] +} diff --git a/packages/plugins/grpc/tsconfig.json b/packages/plugins/grpc/tsconfig.json new file mode 100644 index 00000000..671eae1a --- /dev/null +++ b/packages/plugins/grpc/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@artusx/tsconfig", + "compilerOptions": { + "baseUrl": ".", + "types": ["node", "jest", "reflect-metadata"], + "outDir": "lib" + }, + "include": ["src/**/*.ts", "src/**/*.json", "test/**/*.ts"] +} diff --git a/rush.json b/rush.json index 414876cc..130e1dab 100644 --- a/rush.json +++ b/rush.json @@ -506,6 +506,11 @@ "projectFolder": "packages/apps/artusx-koa-bench", "tags": ["artusx-apps"] }, + { + "packageName": "artusx-grpc", + "projectFolder": "packages/apps/artusx-grpc", + "tags": ["artusx-apps"] + }, // plugins { "packageName": "@artusx/plugin-redis", @@ -597,6 +602,13 @@ "tags": ["artusx-plugins"], "shouldPublish": true, "versionPolicyName": "public" + }, + { + "packageName": "@artusx/plugin-grpc", + "projectFolder": "packages/plugins/grpc", + "tags": ["artusx-plugins"], + "shouldPublish": true, + "versionPolicyName": "public" } ] }