smagafurov / fastapi-jsonrpc Goto Github PK
View Code? Open in Web Editor NEWJSON-RPC server based on fastapi
License: MIT License
JSON-RPC server based on fastapi
License: MIT License
Nice library! I was wondering if there are any plans to also support websockets (which is a nice extra fastapi provides), like these libraries do?:
https://jsonrpcclient.readthedocs.io/en/latest/
https://github.com/codemation/easyrpc
openapi tests failed after fastapi 0.64 released: https://github.com/tiangolo/fastapi/releases/tag/0.64.0
Is this project Open Source? If you don't add a license, it's not Open Source even if the code is public. By default, everything is copyrighted (all right reserved) even if not explicitly written. That's why there are licenses that circumvent that.
Currently it's impossible to set headers/cookies/status code via FastAPI response param
reason is: ignored sub_response
variable returned from request dependency resolver.
currently hot fix seems not possible because response object is creating outside scope of solve_dependencies
call
Live logs are not captured for anything that happens in JSON-RPC views when running pytest (logging_middleware
from README also doesn't output anything when running in pytest
). But logs are present when running web app.
Requirements:
uvicorn==0.17.6
fastapi==0.75.2
fastapi-jsonrpc==2.2.0
# checked on "pytest<7" too
pytest==7.1.2
# checked on "pytest-asyncio<0.16" too
pytest-asyncio==0.18.3
# checked on "httpx<0.20" too
httpx==0.22.0
pytest.ini
[pytest]
log_cli = True
log_cli_level = INFO
asyncio_mode=auto
main.py
import logging
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger(__name__)
import fastapi_jsonrpc as jsonrpc
from pydantic import BaseModel
from fastapi import Body
app = jsonrpc.API()
api_v1 = jsonrpc.Entrypoint("/api/v1/jsonrpc")
@app.get("/hello")
def handle_hello(name: str):
log.info("hello %s", name)
return {"hello": name}
class MyError(jsonrpc.BaseError):
CODE = 9001
MESSAGE = 'My error'
class DataModel(BaseModel):
details: str
@api_v1.method(errors=[MyError])
def echo(
data: str = Body(..., example='123'),
) -> str:
log.info("Process echo view, data: %s", data)
if data == 'error':
raise MyError(data={'details': 'error'})
else:
return data
app.bind_entrypoint(api_v1)
test_main.py
import logging
from pytest import fixture, mark
from httpx import AsyncClient
from main import app
log = logging.getLogger(__name__)
pytestmark = mark.asyncio
@fixture
async def async_client():
async with AsyncClient(app=app, base_url="http://test") as ac:
yield ac
async def test_echo_jsonrpc(async_client):
url = "/api/v1/jsonrpc"
log.info("gonna run async test for JSON-RPC")
response = await async_client.post(url)
assert response.status_code == 200
log.info("async test for JSON-RPC done")
async def test_hello(async_client):
url = "/hello"
name = "John"
log.info("gonna run async test for hello view")
response = await async_client.get(url, params={"name": name})
assert response.status_code == 200
log.info("async test for hello view done")
uvicorn "main:app"
curl -X 'GET' \
'http://127.0.0.1:8000/hello?name=John' \
-H 'accept: application/json'
{"hello":"John"}%
INFO:main:hello John
INFO: 127.0.0.1:51376 - "GET /hello?name=John HTTP/1.1" 200 OK
The INFO:main:hello John
log comes from the handle_hello
view function
5) call a JSON-RPC FastAPI view:
curl -X 'POST' \
'http://127.0.0.1:8000/api/v1/jsonrpc' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 0,
"method": "echo",
"params": {
"data": "spam and eggs"
}
}'
{"jsonrpc":"2.0","result":"spam and eggs","id":0}%
INFO:main:Process echo view, data: spam and eggs
INFO: 127.0.0.1:51388 - "POST /api/v1/jsonrpc HTTP/1.1" 200 OK
The INFO:main:Process echo view, data: spam and eggs
log comes from the echo
JSON-RPC view function
pytest -s -v
========================= test session starts =========================
platform darwin -- Python 3.9.9, pytest-7.1.2, pluggy-1.0.0 -- /Users/suren/Projects/fastapi-pytest-logging/venv/bin/python
cachedir: .pytest_cache
rootdir: /Users/suren/Projects/fastapi-pytest-logging, configfile: pytest.ini
plugins: asyncio-0.18.3, anyio-3.5.0
asyncio: mode=auto
collected 2 items
test_main.py::test_echo_jsonrpc
---------------------------- live log call ----------------------------
INFO test_main:test_main.py:21 gonna run async test for JSON-RPC
INFO test_main:test_main.py:24 async test for JSON-RPC done
PASSED
test_main.py::test_hello
---------------------------- live log call ----------------------------
INFO test_main:test_main.py:30 gonna run async test for hello view
INFO main:main.py:18 hello John
INFO test_main:test_main.py:33 async test for hello view done
PASSED
========================== 2 passed in 0.14s ==========================
test_echo_jsonrpc
is missing the INFO:main:Process echo view
log, but test_hello
contains the INFO:main:hello
log
Is this fixable via some configs, or does this need to be fixed inside the lib?
Hi,
Fastapi offers a router functionality detailed here.
It can be really useful to split the methods into many files.
It would be nice to be able to do the same things with the JSONRPC methods.
Currently, since Entrypoint
inherits from fastapi's APIRouter
, trying to bind another Entrypoint
with the same path won't allow access to the methods from the second Entrypoint
.
There should be a way to bind 2 Entrypoint
with the same path to one app, allowing both of their methods to be considered.
Really great library btw, good work!
I'd like to use a more up to date version of FastAPI, notably 0.58.0 which was just released and adds the capability to specify servers in the OpenAPI docs.
However, due to the dep requirement of fastapi-jsonrpc, I can't install anything greater than 0.55.x:
[SolverProblemError]
Because fastapi-jsonrpc (0.2.4) depends on fastapi (>=0.55.1,<0.56.0)
and no versions of fastapi-jsonrpc match >0.2.4,<0.3.0, fastapi-jsonrpc (>=0.2.4,<0.3.0) requires fastapi (>=0.55.1,<0.56.0).
So, because my-api depends on both fastapi (^0.58.0) and fastapi-jsonrpc (^0.2.4), version solving failed.
Can the dependency requirement be loosened to allow all versions of FastAPI, perhaps below 1.0? i.e. >=0.55.1,<1.0
?
В fastapi_jsonrpc при валидации запроса ошибка ValidationError обрабатывается по разному если ValidationError выскакивает при инициализации модели, отдаётся ошибка InvalidParams: Period(period_start='2023-12-12', period_end='bad_date') => отдаётся InvalidParams
{
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "Invalid params",
"data": {
"errors": [
{
"loc": [
"period_start"
],
"msg": "invalid date format",
"type": "value_error.date"
}
]
}
},
"id": "0"
}
если ошибка возникает в самописном валидаторе, то ошибка ValidationError никак не обрабатывается, падает ошибка pydantic.ValidationError
Хотелось бы получать одинаковую ошибку InvalidParams
As of version 0.100.0, Fastapi now handles Pydantic v2.
There is at least one issue preventing it to work with this package:
fastapi_jsonrpc/__init__.py:13
ImportError: cannot import name 'DictError' from 'pydantic'
From what I have tested, removing the missing imports or trying to import pydantic models from pydantic.v1
is not enough, this will probably require some design update
Add docs for Params usage
Currently this library is limited to not to be used with FastAPI greater then 0.80 but FastAPI has already released ver. 0.86. Is it possible to change this limitation, please?
pyproject.toml
fastapi = ">0.55,<0.80"
Hi! Great library. But I stumbled upon an inconsistency.
Is this expected behavior that:
import fastapi_jsonrpc as jsonrpc
api_v1 = jsonrpc.Entrypoint("/rpc", tags=["jsonrpc"])
@api_v1.method()
async def create_link(data: dict):
pass
doesn't make it so create_link
receives a tag "jsonrpc"?
Looks like a bug to me.
pip freeze:
aiojobs==1.1.0
annotated-types==0.5.0
anyio==3.7.1
async-timeout==4.0.2
click==8.1.6
exceptiongroup==1.1.2
fastapi==0.100.1
fastapi-jsonrpc==2.5.0
h11==0.14.0
httptools==0.6.0
idna==3.4
pydantic==1.10.0
pydantic_core==2.4.0
python-dotenv==1.0.0
PyYAML==6.0.1
sniffio==1.3.0
starlette==0.27.0
typing_extensions==4.7.1
uvicorn==0.23.2
uvloop==0.17.0
watchfiles==0.19.0
websockets==11.0.3
Is it possible to use ORJSONResponse
(https://fastapi.tiangolo.com/advanced/custom-response/#use-orjsonresponse) in requests?
If not, are there any plans to add this feature?
We encountered a problem with very long serialization in JSON. For example, for 2MB (output JSON) we have serialization overhead > 1 sec (in framework layer) 😧
I understand that I can add examples to endpoints via
@api.method()
def endpoint(value1: str = Body(example='test'), value2: int = Body(example=3))
...
What to do if I want to add multiple examples as discussed in https://fastapi.tiangolo.com/tutorial/schema-extra-example/?h=examples#body-with-multiple-examples ? The obvious thing to do would be to use list the examples in the Body, as it is done in plain FastAPI, but that doesn't work. Perhaps the Entrypoint#method
method can accept an examples parameter (i.e. add it to the MethodRoute
class)?
So I want to use you jsonrpc implementation for its numerous benefits, but when I try to combine it with some functions that return JSON strings I cannot seem to get the response configuration right.
When I run it with json return type I get an error, when I try to use a class implementing BaseModel and containing the json element, then it runs the server but responses error the server out.
If you want to see the code, its basically:
from pydantic import json
@api_v1.method()
def consistency(past_root:str) -> json:
which I would be fine removing the json return type, but the value it responds with to the client should absolutely be this json string. I feel that I am missing something, being a beginner at fastapi and your library as well. The code in question can be found here.
Hi,
Does this library support the usage of the query parameters?
(example: POST http://127.0.0.1:8000/api/v1/jsonrpc?skip=10)
If yes, can you provide an example?
Thank you!
Fabio
fastapi==0.103.1
It's not giving Validation error (code 6000)
for invalid parameters as it was before. Now they are Internal error (code -32603)
instead because something breakes in library error processing.
Downgrading fastapi
version to 0.98.0
helps to mitigate this error.
This code block no longer works
fastapi-jsonrpc/fastapi_jsonrpc/__init__.py
Lines 391 to 397 in 2049e1c
fastapi
introduced some kind of error wrapper and some errors (not all of them) are not just plain dicts anymore.
Here is my traceback for the error
Traceback (most recent call last):
File "/home/ave_satan/.pyenv/versions/3.11.1/envs/statements/lib/python3.11/site-packages/fastapi_jsonrpc/__init__.py", line 596, in _handle_exception
yield
File "/home/ave_satan/.pyenv/versions/3.11.1/envs/statements/lib/python3.11/site-packages/fastapi_jsonrpc/__init__.py", line 1147, in handle_req_to_resp
resp = await self.handle_req(
^^^^^^^^^^^^^^^^^^^^^^
File "/home/ave_satan/.pyenv/versions/3.11.1/envs/statements/lib/python3.11/site-packages/fastapi_jsonrpc/__init__.py", line 1173, in handle_req
return await route.handle_req(
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ave_satan/.pyenv/versions/3.11.1/envs/statements/lib/python3.11/site-packages/fastapi_jsonrpc/__init__.py", line 865, in handle_req
raise invalid_params_from_validation_error(RequestValidationError(errors))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ave_satan/.pyenv/versions/3.11.1/envs/statements/lib/python3.11/site-packages/fastapi_jsonrpc/__init__.py", line 393, in invalid_params_from_validation_error
if err['loc'][:1] == ('body', ):
~~~^^^^^^^
TypeError: list indices must be integers or slices, not str
fastapi-jsonrpc/fastapi_jsonrpc/__init__.py
Line 391 in 2049e1c
exc.errors()
on 0.98.0
:
[{'loc': ('body', 'accounts', 0, 'bank_code'), 'msg': 'none is not an allowed value', 'type': 'type_error.none.not_allowed'}]
exc.errors()
on 0.103.1
:
[[ErrorWrapper(exc=ValidationError(model='Account', errors=[{'loc': ('bank_code',), 'msg': 'none is not an allowed value', 'type': 'type_error.none.not_allowed'}]), loc=('body', 'accounts', 0))]]
According to JSON-RPC 2.0 Specification , the params
field of request object may be either by-position (Array) or by-name (Object).
However the current implementation of fastapi-jsonrpc forces to use by-name parameter:
fastapi-jsonrpc/fastapi_jsonrpc/__init__.py
Line 369 in 1329be6
This causes this awesome library is not usable in some circumstances. Can we support by-position parameters in the future version?
Hey folks
Great lib and I'm using it with my projects quite often now
I saw one strange behavior that I suppose needs some fixing or enhancement
Here I'm sending a simple request via postman and getting a blank response:
Request:
{
"jsonrpc": "2.0",
"params": {
"invite_id": "fb233510-9862-4f98-8278-8a173841857e"
},
"method": "verify_invite"
}
As you can see id
field is missing on the JSON body, but no error is raised if it's missing there
When I'm adding id
to the body I got the correct JSON response:
Request:
{
"jsonrpc": "2.0",
"params": {
"invite_id": "fb233510-9862-4f98-8278-8a173841857e"
},
"method": "verify_invite",
"id": 0
}
Maybe I'm missing something and you can point me In the right direction of using things, but for me, it looks like there should be an error raised when id
is not passed to the JSON body
I tried to write swagger doc with mulitple samples in request body but failed.
Here is my code:
@api_v1.method()
def echo(
data: str = Body(..., examples=['123', '456']),
) -> str:
return data
And here is what swagger ui doc shows.
What I want is a dropdown combobox which include multiple samples.
Does fastapi-jsonrpc support multiple samples?
The problem:
clone_dependant()
seems to copy the dependant, but it's not. When you try to append something to cloned dependant's fields you edit the original fields too.
cloned_dependant = clone_dependant(original_dependant)
original_dependant.body_params.append("some value that should be in copied")
assert cloned_dependant.body_params == ["some path"] # fails
Following release 0.18.0 of Starlette accepted by FastAPI in 0.76.0, starlette.Response
object does not accept None
value anymore for status_code
(starlette/responses.py#L77).
This breaks the EntrypointRoute.handle_http_request function.
A fix might be possible, but freezing dependencies would be safer to avoid future issue with future updates.
To reproduce:
This minimal example does not work anymore
# main.py
import fastapi_jsonrpc as jsonrpc
app = jsonrpc.API()
api_v1 = jsonrpc.Entrypoint('/api/v1/jsonrpc')
@api_v1.method()
def echo(
x: int,
) -> int:
return x
app.bind_entrypoint(api_v1)
if __name__ == '__main__':
import uvicorn
uvicorn.run('main:app', port=5000, debug=True, access_log=False)
pip install fastapi==0.76.0
python main.py
curl --request POST \
--url http://localhost:5000/api/v1/jsonrpc \
--header 'Content-Type: application/json' \
--data '{
"method": "echo",
"id": "1",
"params": {
"x": 1
}
}'
FastApi adds the 'additionalProp1' field to the example code during tryout, which naturally does not pass validation during execution.
there is no problem on version 0.97.0
Hi, colleagues! Sometime I have non-json requests in my handlers (like form with file inside) and application crashes with UnicodeDecodeError. I think this error is ok to catch in parse_body and throw ParseError
.
В swagger в примере запросе поле "jsonrpc" выставляется равным 2, как число. Необходимо, чтобы выставлялось "2.0", как строка.
Есть предположение что, если тут значение example='"2.0"', то тогда swagger автоматом не преобразовывал бы это значение в численное.
Версия библиотеки fastapi-jsonrpc==2.0.2, pydantic==1.7.3
Because client makes post on only one route it's hard to tell what is request
Maybe it's not about this library, but it would be really useful to have this feature out of the box.
I just encountered fastapi-jsonrpc and when trying the provided example:
import fastapi_jsonrpc as jsonrpc
from pydantic import BaseModel
from fastapi import Body
app = jsonrpc.API()
api_v1 = jsonrpc.Entrypoint('/api/v1/jsonrpc')
class MyError(jsonrpc.BaseError):
CODE = 5000
MESSAGE = 'My error'
class DataModel(BaseModel):
details: str
@api_v1.method(errors=[MyError])
def echo(
data: str = Body(..., example='123'),
) -> str:
if data == 'error':
raise MyError(data={'details': 'error'})
else:
return data
app.bind_entrypoint(api_v1)
if __name__ == '__main__':
import uvicorn
uvicorn.run('example1:app', port=5000, debug=True, access_log=False)
it seems that the example value '123' of method echo
does not end up correctly in the schema.
http://127.0.0.1:5000/docs shows this example
{
"jsonrpc": "2.0",
"id": 0,
"method": "echo",
"params": {
"data": "string"
}
}
where I would have expected it to be
{
"jsonrpc": "2.0",
"id": 0,
"method": "echo",
"params": {
"data": "123"
}
}
Is there a way to provide an example value for a parameter?
Note: I would like it to be just an example value, it should not be used as a default value when the user omits it. E.g. data: str = '123'
does show '123'
as an example value, but it would also act as a default value when the request omits the data
parameter.
Prance gives errors like:
Processing "https://.../openapi.json"...
-> Resolving external references.
[ValidationError]: ("{'content': {'application/json': {'schema': {'title': '_Request', 'required': ['method', 'params'], 'type': 'object', 'properties': {'jsonrpc': {'title': 'Jsonrpc', 'type': 'string', 'example': '2.0', 'const': '2.0'}, 'id': {'title': 'Id', 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'example': 0}, 'method': {'title': 'Method', 'type': 'string'}, 'params': {'title': 'Params', 'type': 'object'}}, 'additionalProperties': False}}}, 'required': True} is not valid under any of the given schemas", 'oneOf', deque(['paths', '/rpc', 'post', 'requestBody']), None, [<ValidationError: "{'title': '_Request', 'required': ['method', 'params'], 'type': 'object', 'properties': {'jsonrpc': {'title': 'Jsonrpc', 'type': 'string', 'example': '2.0', 'const': '2.0'}, 'id': {'title': 'Id', 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'example': 0}, 'method': {'title': 'Method', 'type': 'string'}, 'params': {'title': 'Params', 'type': 'object'}}, 'additionalProperties': False} is not valid under any of the given schemas">, <ValidationError: "'$ref' is a required property">], [{'$ref': '#/definitions/RequestBody'}, {'$ref': '#/definitions/Reference'}], {'content': {'application/json': {'schema': {'title': '_Request', 'required': ['method', 'params'], 'type': 'object', 'properties': {'jsonrpc': {'title': 'Jsonrpc', 'type': 'string', 'example': '2.0', 'const': '2.0'}, 'id': {'title': 'Id', 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'example': 0}, 'method': {'title': 'Method', 'type': 'string'}, 'params': {'title': 'Params', 'type': 'object'}}, 'additionalProperties': False}}}, 'required': True}, {'oneOf': [{'$ref': '#/definitions/RequestBody'}, {'$ref': '#/definitions/Reference'}]}, deque(['properties', 'paths', 'patternProperties', '^\\/', 'patternProperties', '^(get|put|post|delete|options|head|patch|trace)$', 'properties', 'requestBody', 'oneOf']), None)
It makes impossible use of model generators for pydantic https://pydantic-docs.helpmanual.io/datamodel_code_generator/
Hi,
With the following test code:
import fastapi_jsonrpc as jsonrpc
app = jsonrpc.API()
api = jsonrpc.Entrypoint("/")
@api.method()
def simple() -> None:
return None
app.bind_entrypoint(api)
executed as bin/uvicorn example_service:app --port 6666
and accessed using curl, we get the following:
$ curl -sX POST -d '{"jsonrpc": "2.0", "id": 0, "method": "simple", "params":[]}' -H 'Content-Type: application/json' http://127.0.0.1:6666/ | jq
{
"jsonrpc": "2.0",
"result": null,
"id": 0
}
But if we try calling without a params element (as described legal in the JSON RPC spec, emphasis mine) - https://www.jsonrpc.org/specification#request_object
params
A Structured value that holds the parameter values to be used during the invocation of the method. This member MAY be omitted.
We get the following:
curl -sX POST -d '{"jsonrpc": "2.0", "id": 0, "method": "simple"}' -H 'Content-Type: application/json' http://127.0.0.1:6666/ | jq
{
"jsonrpc": "2.0",
"error": {
"code": -32600,
"message": "Invalid Request",
"data": {
"errors": [
{
"loc": [
"params"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
},
"id": 0
}
I believe this may be able to be fixed by changing https://github.com/smagafurov/fastapi-jsonrpc/blob/master/fastapi_jsonrpc/__init__.py#L368 from:
params: dict
to
params: dict = Field(default_factory=dict)
But I'm not 100% sure what follow-on effects this may have (if any).
Hey!
I've found this lib quite great to combine both fastapi and rpc together. Great work!
But I'm struggling with only one thing: if I'm trying to define rpc methods outside the python file where the entrypoint is defined - it leads to a "Method not found" error on the response.
Maybe I'm doing something wrong, so you can point me where exactly I am wrong
app.py
app = API(name=settings.app_name, debug=settings.debug)
jsonrpc = Entrypoint('/jsonrpc', middlewares=[rpc_logging_middleware])
app.bind_entrypoint(jsonrpc)
rpc.py
from fastapi_jsonrpc import BaseModel, BaseError, Body
from app import jsonrpc
@jsonrpc.method(name="echo")
async def echo(
data: str = Body(..., example='123'),
) -> str:
if data == 'error':
raise MyError(data={'details': 'error'})
else:
return data
And the request/response example with my setup:
# request:
{
"jsonrpc": "2.0",
"method": "echo",
"params": {
"data": "test"
},
"id": 1
}
# response:
{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found"
},
"id": 1
}
Looking forward to any advice or help here!
Thanks
Can this module work with Websocket FastAPI roure?
My pytest
run failed due to a DeprecationWarning
. I can suppress this for now, but was wondering if a fix for this is possible?
app.bind_entrypoint(api_v1)
.venv\Lib\site-packages\fastapi_jsonrpc\__init__.py:1528: in bind_entrypoint
self.on_event('shutdown')(ep.shutdown)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
args = (<fastapi_jsonrpc.API object at 0x000002034CAF4290>, 'shutdown'), kwargs = {}
@functools.wraps(arg)
def wrapper(*args, **kwargs):
> warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
E DeprecationWarning:
E on_event is deprecated, use lifespan event handlers instead.
E
E Read more about it in the
E [FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/).
.venv\Lib\site-packages\typing_extensions.py:2498: DeprecationWarning
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.