pavlov99 / json-rpc

🔁 JSON-RPC 1/2 transport implementation. Supports python 2/3 and pypy.
http://json-rpc.readthedocs.org
MIT License
465 stars 100 forks source link

Add support for flask blueprints subdomains #80

Open biozz opened 6 years ago

biozz commented 6 years ago

Description

Currently it is not possible to natively use flask blueprints subdomains wildcards, because JSONRPCAPI.jsonrpc method in flask backend doesn't handle additional argument if blueprint subdomain is defined as a wildcard, i.e.

    app.register_blueprint(api.as_blueprint(), subdomain='<subdomain>')

In this way, the api can be used through api.example.com or api2.example.com or other.example.com.

Steps to Reproduce

  1. Define api endpoint
  2. Register endpoint as a blueprint with wildcard subdomain
  3. Perform call to an endpoint

Expected behavior: api can be used through registered endpoint and subdomain wildcard is available in the jsonrpc method.

Actual behavior: jsonrpc raises an exception, that it can't accept additional argument, i.e. subdomain.

Reproduces how often: always

Versions

Additional Information

I can see there are multiple ways this can be solved. For now I am using a workaround to handle subdomains in the application, which involves nginx and setting some headers. I would also like to contribute and make this possible, what are your thoughts on this feature?

biozz commented 6 years ago

Hi! After some digging through the code I found quite simple solution to this. All I had to do was to add **kwargs into jsonrpc and jsonrpc_map methods in backend/flask, so Flask can pass the subdomain argument in.

def jsonrpc(self, **kwargs):
        request_str = self._get_request_str()
        ...

def jsonrpc_map(self, **kwargs):
        """ Map of json-rpc available calls.
        ...

That way these methods will not fail as I mentioned before:

TypeError: jsonrpc() got an unexpected keyword argument 'subdomain'
TypeError: jsonrpc_map() got an unexpected keyword argument 'subdomain'

And now subdomain name can be accessed through Flask's request.view_args inside jsonrpc method.

I can submit a PR for this, but I might need some help with the test, because it seems to fail silently.

We will need a new client, which will register blueprint with wildcard subdomain and define SERVER_NAME for the subdomains to resolve properly.

def _get_test_client_subdomain(self, api):
        @api.dispatcher.add_method
        def dummy():
            return ""

        app = Flask(__name__)
        app.config["TESTING"] = True
        app.config["SERVER_NAME"] = "example.com"
        app.register_blueprint(api.as_blueprint(), subdomain='<subdomain>')
        return app.test_client()

And the test itself, similar to test_client:

def test_subdomain(self):
        response = self.client_subdomain.post(
            '/',
            data=self.REQUEST,
            content_type='application/json',
            headers={'host': 'test.example.com'}
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data.decode('utf8'))
        self.assertEqual(data['result'], '')

It passes even if I put example.com in SERVER_NAME and examples.com in host headers, instead it should return Not found error when names are mismatched.

Please let me know what you think about this. I forked your repo and pushed these changes, please have a look. Thank you!