Metadata-Version: 2.1
Name: jj
Version: 2.0.0.dev5
Summary: Remote HTTP Mock
Home-page: https://github.com/nikitanovosibirsk/jj
Author: Nikita Tsvetkov
Author-email: nikitanovosibirsk@yandex.com
License: Apache-2.0
Description: # jj
        
        
        [![Codecov](https://img.shields.io/codecov/c/github/nikitanovosibirsk/jj/master.svg?style=flat-square)](https://codecov.io/gh/nikitanovosibirsk/jj)
        [![PyPI](https://img.shields.io/pypi/v/jj/2.0.0-dev.5.svg?style=flat-square)](https://pypi.python.org/pypi/jj/2.0.0-dev.5)
        [![PyPI - Downloads](https://img.shields.io/pypi/dm/jj?style=flat-square)](https://pypi.python.org/pypi/jj/2.0.0-dev.5)
        [![Python Version](https://img.shields.io/pypi/pyversions/jj/2.0.0-dev.5.svg?style=flat-square)](https://pypi.python.org/pypi/jj/2.0.0-dev.5)
        
        ## Installation
        
        ```bash
        pip3 install jj==2.0.0-dev.5
        ```
        
        ## Usage
        
        ```python
        import jj
        
        @jj.match("*")
        async def handler(request: jj.Request) -> jj.Response:
            return jj.Response(body="200 OK")
        
        jj.serve()
        ```
        
        ## Documentation
        
        * [Matchers](#matchers)
          * [Method](#method)
            * [match_method(`method`)](#match_methodmethod)
          * [Path](#path)
            * [match_path(`path`)](#match_pathpath)
          * [Segments](#segments)
          * [Params](#params)
            * [match_param(`name`, `val`)](#match_paramname-val)
            * [match_params(`params`)](#match_paramsparams)
          * [Headers](#headers)
            * [match_header(`name`, `val`)](#match_headername-val)
            * [match_headers(`headers`)](#match_headersheaders)
          * [Combining Matchers](#combining-matchers)
            * [match_any(`matchers`)](#match_anymatchers)
            * [match_all(`matchers`)](#match_allmatchers)
            * [match(`method`, `path`, `params`, `headers`)](#matchmethod-path-params-headers)
        * [Responses](#responses)
          * [Response](#response)
            * [JSON Response](#json-response)
            * [HTML Response](#html-response)
            * [Binary Response](#binary-response)
            * [Not Found Response](#not-found-response)
            * [Predefined Body](#predefined-body)
          * [StaticResponse](#staticresponse)
            * [Inline Content](#inline-content)
            * [Downloadable File](#downloadable-file)
          * [RelayResponse `β`](#relayresponse-β)
        * [Apps](#apps)
          * [Single App](#single-app)
          * [Multiple Apps](#multiple-apps)
          * [App Inheritance](#app-inheritance)
        * [Middlewares](#middlewares)
          * [Handler Middleware](#handler-middleware)
          * [App Middleware](#app-middleware)
        
        ---
        
        ### Matchers
        
        #### Method
        
        ##### match_method(`method`)
        
        ```python
        from jj.http.methods import ANY, GET, POST
        
        @jj.match_method(GET)
        async def handler(request):
            return jj.Response(body="Method: " + request.method)
        ```
        
        #### Path
        
        ##### match_path(`path`)
        
        ```python
        @jj.match_path("/users")
        async def handler(request):
            return jj.Response(body="Path: " + request.path)
        ```
        
        #### Segments
        
        ```python
        @jj.match_path("/users/{users_id}")
        async def handler(request):
            return jj.Response(body=f"Segments: {request.segments}")
        ```
        
        More information available here https://docs.aiohttp.org/en/stable/web_quickstart.html#variable-resources
        
        #### Params
        
        ##### match_param(`name`, `val`)
        
        ```python
        @jj.match_param("locale", "en_US")
        async def handler(request):
            locales = request.params.getall('locale')
            return jj.Response(body="Locales: " + ",".join(locales))
        ```
        
        ##### match_params(`params`)
        
        ```python
        @jj.match_params({"locale": "en_US", "timezone": "UTC"})
        async def handler(request):
            # Literal String Interpolation (PEP 498)
            return jj.Response(body=f"Params: {request.params}")
        ```
        
        #### Headers
        
        #####  match_header(`name`, `val`)
        
        ```python
        @jj.match_header("X-Forwarded-Proto", "https")
        async def handler(request):
            proto = request.headers.getone("X-Forwarded-Proto")
            return jj.Response(body="Proto: " + proto)
        ```
        
        ##### match_headers(`headers`)
        
        ```python
        @jj.match_headers({
            "x-user-id": "1432",
            "x-client-id": "iphone",
        })
        async def handler(request):
            return jj.Response(body=f"Headers: {request.headers}")
        ```
        
        #### Combining Matchers
        
        ##### match_any(`matchers`)
        
        ```python
        from jj.http import PATCH, PUT
        
        @jj.match_any([
            jj.match_method(PUT),
            jj.match_method(PATCH),
        ])
        async def handler(request):
            return jj.Response(body="200 OK")
        ```
        
        ##### match_all(`matchers`)
        
        ```python
        @jj.match_all([
            jj.match_method("*"),
            jj.match_path("/"),
            jj.match_params({"locale": "en_US"}),
            jj.match_headers({"x-request-id": "0fefbf48"}),
        ])
        async def handler(request):
            return jj.Response(body="200 OK")
        ```
        
        ##### match(`method`, `path`, `params`, `headers`)
        
        ```python
        @jj.match("*", "/", {"locale": "en_US"}, {"x-request-id": "0fefbf48"})
        async def handler(request):
            return jj.Response(body="200 OK")
        ```
        
        ---
        
        ### Responses
        
        #### Response
        
        ##### JSON Response
        
        ```python
        @jj.match("*")
        async def handler(request):
            return jj.Response(json={"message": "200 OK"})
        ```
        
        ##### HTML Response
        
        ```python
        @jj.match("*")
        async def handler(request):
            return jj.Response(body="<p>text<p>", headers={"Content-Type": "text/html"})
        ```
        
        ##### Binary Response
        
        ```python
        @jj.match("*")
        async def handler(request):
            return jj.Response(body=b"<binary>")
        ```
        
        ##### Not Found Response
        
        ```python
        @jj.match("*")
        async def handler(request):
            return jj.Response(status=404, reason="Not Found")
        ```
        
        ##### Predefined Body
        
        ```python
        from jj.http import GET
        
        @jj.match(GET, "/users")
        async def handler(request):
            return jj.Response(body=open("responses/users.json", "rb"))
        ```
        
        ```python
        from jj.http import POST, CREATED
        
        @jj.match(POST, "/users")
        async def handler(request):
            return jj.Response(body=open("responses/created.json", "rb"), status=CREATED)
        ```
        
        #### StaticResponse
        
        ##### Inline Content
        
        ```python
        from jj.http import GET
        
        @jj.match(GET, "/image")
        async def handler(request):
            return jj.StaticResponse("public/image.jpg")
        ```
        
        ##### Downloadable File
        
        ```python
        from jj.http import GET
        
        @jj.match(GET, "/report")
        async def handler(request):
            return jj.StaticResponse("public/report.csv", attachment=True)
        ```
        
        ```python
        from jj.http import GET
        
        @jj.match(GET, "/")
        async def handler(request):
            return jj.StaticResponse("public/report.csv", attachment="report.csv")
        ```
        
        For more information visit https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
        
        #### RelayResponse `β`
        
        ```python
        @jj.match("*")
        async def handler(request):
            return jj.RelayResponse(target="https://httpbin.org/")
        ```
        
        ---
        
        ### Apps
        
        #### Single App
        
        ```python
        import jj
        from jj.http.methods import GET, ANY
        from jj.http.codes import OK, NOT_FOUND
        
        class App(jj.App):
            @jj.match(GET, "/")
            async def root_handler(self, request: jj.Request) -> jj.Response:
                return jj.Response(status=OK, json={"message": "200 OK"})
        
            @jj.match(ANY)
            async def default_handler(self, request: jj.Request) -> jj.Response:
                return jj.Response(status=NOT_FOUND, json={"message": "Not Found"})
        
        jj.serve(App(), port=5000)
        ```
        
        #### Multiple Apps
        
        ```python
        import jj
        
        class App(jj.App):
            @jj.match("*")
            async def handler(self, request: jj.Request) -> jj.Response:
                return jj.Response(body="App")
        
        class AnotherApp(jj.App):
            @jj.match("*")
            async def handler(self, request: jj.Request) -> jj.Response:
                return jj.Response(body="AnotherApp")
        
        jj.start(App(), port=5001)
        jj.start(AnotherApp(), port=5002)
        
        jj.wait_for([KeyboardInterrupt])
        ```
        
        #### App Inheritance
        
        ```python
        import jj
        
        class UsersApp(jj.App):
            @jj.match("*", path="/users")
            async def handler(self, request: jj.Request) -> jj.Response:
                return jj.Response(body="Users")
        
        class GroupsApp(jj.App):
            @jj.match("*", path="/groups")
            async def handler(self, request: jj.Request) -> jj.Response:
                return jj.Response(body="Groups")
        
        class App(UsersApp, GroupsApp):
            pass
        
        jj.serve(App())
        ```
        
        ### Middlewares
        
        #### Handler Middleware
        
        ```python
        import jj
        from jj.http.codes import OK, FORBIDDEN
        
        class Middleware(jj.Middleware):
            async def do(self, request, handler, app):
                if request.headers.get("x-secret-key") != "<SECRET_KEY>":
                    return jj.Response(status=FORBIDDEN, body="Forbidden")
                return await handler(request)
        
        class App(jj.App):
            @Middleware()
            @jj.match("*")
            async def handler(self, request: jj.Request) -> jj.Response:
                return jj.Response(status=OK, body="Ok")
        
        jj.serve(App())
        ```
        
        #### App Middleware
        
        ```python
        import jj
        from jj.http.codes import OK, FORBIDDEN
        
        class ReusableMiddleware(jj.Middleware):
            def __init__(self, secret_key):
                super().__init__()
                self._secret_key = secret_key
        
            async def do(self, request, handler, app):
                if request.headers.get("x-secret-key") != self._secret_key:
                    return jj.Response(status=FORBIDDEN, body="Forbidden")
                return await handler(request)
        
        private = ReusableMiddleware("<SECRET_KEY>")
        
        @private
        class App(jj.App):
            @jj.match("*")
            async def handler(self, request: jj.Request) -> jj.Response:
                return jj.Response(status=OK, body="Ok")
        
        jj.serve(App())
        ```
        
Platform: UNKNOWN
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Requires-Python: >=3.6.0
Description-Content-Type: text/markdown
