{ "info": { "author": "Petri Savolainen", "author_email": "petri@koodaamo.fi", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Natural Language :: English", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6" ], "description": "===============================\nhttpreverse\n===============================\n\n.. image:: https://img.shields.io/pypi/v/httpreverse.svg\n :target: https://pypi.python.org/pypi/httpreverse\n\nTool to help reverse-engineer legacy HTTP APIs.\n\nRationale and scope\n--------------------\n\nThis package was born of a need to be able to use multiple different kinds of\nexisting, undocumented legacy HTTP APIs not following any kind of consistent,\nwell planned design. It was also thought helpful to be able to add a little bit\nof documentation about what those legacy APIs do.\n\nThis package is NOT meant for defining new APIs. Use e.g. Swagger for that.\n\nWhy then not just use Swagger or some other such tool? They are really meant for\ncreating new APIs from scratch and as such cater to a bit different use case.\nFor example they tend to be geared toward the verbose. When reverse-engineering\nand documenting existing APIs, all the details are not that important. We just\nneed to make it easy to use the APIs and be able to add an explanation of what\nthey do, rather than documenting everything.\n\nThe examples hopefully clarify the difference and some of the benefits of this\npackage.\n\nNote that this package does NOT make HTTP requests using some client library.\nThat is up to you; use something from the Python standard library, or the\n'requests' package, or something asynchronous, whatever.\n\nAPI specification examples\n------------------------\n\nNote: these examples are illustrative. For working examples, see the tests.\n\n**Simple example**\n\nAn example API definition in YAML that specifies two operations for querying\nsingle and double rooms reservations, respectively::\n\n label: Hotel API\n description: An API to check room reservations\n\n operations:\n\n list-singlerooms:\n label: List single room reservations\n description: List all reserved single rooms\n\n request:\n method: GET\n path: /hotel/reservations\n params:\n size: single\n\n response:\n type: application/json\n parser: hotelapi.util:parseresponse\n\n list-doublerooms:\n label: List double room reservations\n description: List all reserved double rooms\n\n request:\n method: GET\n path: /hotel/reservations\n params:\n size: double\n\n response:\n type: application/json\n parser: hotelapi.parseresponse\n\n\nThis is similar to how many specification syntaxes express HTTP APIs. Clear,\nbut often lots of boilerplate and repetition. Parses into a plain dict using\nPyYaml as-is. Now let's see how to save some effort.\n\n\n**Using Jinja templating for API spec expansion**\n\nThe API document can be expanded using Jinja2 templating. Using our room\nreservation example, we could generate an API operation for each room size\nvariation::\n\n operations:\n\n {% for size in sizes %}\n\n list-{{size}}-rooms:\n label: List {{size}} room reservations\n description: List all reserved {{size}} rooms\n request:\n method: GET\n path: /hotel/reservations\n params:\n size: {{size}}\n\n {% endfor %}\n\nTwo different API operations would be generated, such as with this code, assuming\nthe api spec has been read into a string variable called 'yamlsource':\n\n>>> from httpreverse import expand_jinja\n>>> expanded = expand_jinja(yamlsource, context={\"sizes\":[\"single\", \"double\"]})\n>>>\n\nFor blunt copying of parts of the YAML document to another place, the standard\nYAML anchor/alias mechanism can of course be used as well.\n\n**Templated request specifications**\n\nBesides Jinja templating, a custom templating mechanism is provided for request\nand response specification convenience. Here's an example with a ``roomapi``\nrequest/response template that is used to move repetitive request and response\nspecifications into a common template, referred to from the actual specs::\n\n label: Hotel API\n description: An API to check room reservations\n\n templates:\n\n roomapi:\n request:\n method: GET\n path: /hotel/reservations\n response:\n type: application/json\n parser: hotelapi.parseresponse\n\n operations:\n\n list-singlerooms:\n label: List single room reservations\n description: List all reserved single rooms\n template: roomapi\n request:\n params:\n size: single\n\n list-doublerooms:\n label: List double room reservations\n description: List all reserved double rooms\n template: roomapi\n request:\n params:\n size: double\n\nHere's how to apply the request/response template in Python:\n\n>>> from httpreverse import apply_template\n>>> api = yaml.load(yamlsource)\n>>> templates = api[\"templates\"]\n>>> operation = api[\"operations\"][\"list-doublerooms\"]\n>>> applied = apply_template(operation, templates)\n>>>\n\n**Simple parametrization**\n\nThe API definitions can also be parametrized for convenient run-time use. The\nparametrization function accepts an optional context argument that is simply\na dictionary that is used to assign values to all the named parameters found\nin the operations. Parameters are prefixed with the dollar sign ('$'). So it\nwould be possible to also specify a single dynamically invoked operation for\nlisting the rooms::\n\n operations:\n\n list-rooms:\n label: List room reservations\n description: List reserved rooms\n template: roomapi\n request:\n params:\n size: $size\n\nBy passing either ``{\"size\":\"single\"}`` or ``{\"size\": \"double\"}`` as context,\nroom size values would then be assigned:\n\n>>> from httpreverse import parametrize\n>>> api = yaml.load(yamlsource)\n>>> operation = api[\"operations\"][\"list-rooms\"]\n>>> parametrized = parametrize(operation, context={\"size\":single})\n>>>\n\nMore complex parametrizations are possible using the same simple mechanism::\n\n operations:\n\n add-reservation:\n label: Add reservation\n description: Add a room reservation\n template: roomapi\n request:\n method: POST\n body:\n value: {\"size\": $roomsize, \"customers\": $customers}\n type: application/json\n\nThe context would then have to include both the room size and occupants, ie.\n``{\"roomsize\":\"double\", \"customers\":[\"John Doe\", \"Jane Doe\"]}``.\n\nConsult the YAML documentation for more on what kind of data structures are\npossible to express.\n\nWhen a `type` + `value` is given for a parameter or body (as above), the\nvalue is automatically marshaled to the given type (json in above example).\nIf a parameter or body is given directly (no type+value syntax), a default\nmust be given thus:\n\n defaults:\n\n structured_param_type: json\n structured_body_type: json\n\nThe above API snippet would specify that whenever a structured parameter\nor body value is encountered (such as a container or mapping), it will be\nmarshalled to json. Simple values (strings, numbers etc) are used as-is.\n\n**Request generator and response parser loading**\n\nThere are two convenience functions, ``_load_generator`` for loading the\nrequest generator and ``_load_parser`` for loading the response parser:\n\n>>> from httpreverse import _load_parser\n>>> api = yaml.load(yamlsource)\n>>> parser = _load_parser(api[\"list-rooms\"])\n>>>\n\n**Recommended API operations spec generation and use**\n\nTypically, when using httpreverse to e.g. make http requests using whatever\nhttp client you have, you might want to first run just the Jinja expansion\nfirst and parse the resulting YAML string. Then, apply the request/response\ntemplates for the operations you expect to be using (or maybe all of them).\nKeep a copy of the the result. Finally, for each HTTP request, just parametrize\nthe API operation being used, marshal parameters and body and fire away!\n\n\n=======\nHistory\n=======\n\n0.4.0 (2017-02-26)\n-------------------\n\n* Handle arbitrary data structure parametrization\n* Implement parameter & body marshaling\n* Some backwards-incompatible API changes\n\n0.3.0 (2017-02-20)\n-------------------\n\n* Implement request generator loading (issue #2)\n* Implement response parser loading (issue #1))\n* Implement request conversion (issue #4)\n\n0.2.0 (2017-02-19)\n------------------\n\n* Implement static context specification support (issue #3)\n* Improved documentation\n* Request/response template expansion and spec parametrization\n do their magic on single operations (as they should) rather\n than the full API. Jinja template expansion is kept as-is.\n\n0.1.0 (2017-02-17)\n------------------\n\n* First release on PyPI.\n\n\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/koodaamo/httpreverse", "keywords": "httpreverse", "license": "GNU General Public License v3", "maintainer": "", "maintainer_email": "", "name": "httpreverse", "package_url": "https://pypi.org/project/httpreverse/", "platform": "", "project_url": "https://pypi.org/project/httpreverse/", "project_urls": { "Homepage": "https://github.com/koodaamo/httpreverse" }, "release_url": "https://pypi.org/project/httpreverse/0.4.0/", "requires_dist": [ "jinja2", "pyaml", "xmltodict" ], "requires_python": "", "summary": "Reverse-engineer legacy HTTP APIs", "version": "0.4.0" }, "last_serial": 2669420, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "fd7757f592e05cc904257bd1b425b3ea", "sha256": "4e4c7db21e68d70a1eef6573ca8a806e84ee459e24cc01c96c8371583f599364" }, "downloads": -1, "filename": "httpreverse-0.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "fd7757f592e05cc904257bd1b425b3ea", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 7730, "upload_time": "2017-02-19T16:29:51", "url": "https://files.pythonhosted.org/packages/8f/0f/23b3cc396f751fd705735330787a07b5c4b1162323911e3b776e802b73df/httpreverse-0.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "24e716b1e4154fe62ca9420191b937a5", "sha256": "66d2253db0a22aa4dae651f23927ffe806f043d32faa9c33535e8dd23c9a69e4" }, "downloads": -1, "filename": "httpreverse-0.1.0-py3.6.egg", "has_sig": false, "md5_digest": "24e716b1e4154fe62ca9420191b937a5", "packagetype": "bdist_egg", "python_version": "3.6", "requires_python": null, "size": 6680, "upload_time": "2017-02-19T16:30:02", "url": "https://files.pythonhosted.org/packages/4c/4d/648596907c7f3a9bfec85451ca4a19061c9cd39897b0eff739a3f2c9dc00/httpreverse-0.1.0-py3.6.egg" }, { "comment_text": "", "digests": { "md5": "918e9e230d6644e495d8e4c035137154", "sha256": "d92b794fb3ef11a9bb9bc3fdec93b2e2b6a9f07f4ba0b5b2f3a91d8c1eacf188" }, "downloads": -1, "filename": "httpreverse-0.1.0.tar.gz", "has_sig": false, "md5_digest": "918e9e230d6644e495d8e4c035137154", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9217, "upload_time": "2017-02-19T16:30:14", "url": "https://files.pythonhosted.org/packages/3f/bd/f9f39f2d8d7e0cb044debe20582955bc84f15c8fd7c44edcfdc6653c61c9/httpreverse-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "51301d29523ea1f20fa3a6ce5eab05c6", "sha256": "afe587cdfc97028e8f4f71d7d888352e8dd1ac3027e6a2ebe74e9957047e768f" }, "downloads": -1, "filename": "httpreverse-0.2.0.tar.gz", "has_sig": false, "md5_digest": "51301d29523ea1f20fa3a6ce5eab05c6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9805, "upload_time": "2017-02-19T21:44:21", "url": "https://files.pythonhosted.org/packages/19/7f/d5117d515e1a136652ced3a7cd3f419e3e4576c1d4b3b6bd97141c16171f/httpreverse-0.2.0.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "bc5dfe0801a77dc17cbc611708d1c638", "sha256": "8344f6e832ae1f7a67667436772622b5d6678f3b62bf8d7cf0602654db09aee8" }, "downloads": -1, "filename": "httpreverse-0.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "bc5dfe0801a77dc17cbc611708d1c638", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 9789, "upload_time": "2017-02-20T11:10:18", "url": "https://files.pythonhosted.org/packages/00/a2/6409cee499acbecfa012ac6504a4f00836077c168463b88a8c0a589b4b98/httpreverse-0.3.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "487780d6c16fd3a85718b870c0b73356", "sha256": "e04b41954769924f9d3c32bbe5ff591c83ddda04298338fd92aa4908b58b5866" }, "downloads": -1, "filename": "httpreverse-0.3.0.tar.gz", "has_sig": false, "md5_digest": "487780d6c16fd3a85718b870c0b73356", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11129, "upload_time": "2017-02-20T11:10:19", "url": "https://files.pythonhosted.org/packages/b3/84/06e743f0df7c891154fe120c6db1f8aa7c62c22688ad28749b3e104cd5f6/httpreverse-0.3.0.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "6065f3ea7a40f2b7700ca00284368e13", "sha256": "359097d60bea20746ee7ea1f0b6af1b89936ad5b6263f8f73e6cf9bb5f40dc80" }, "downloads": -1, "filename": "httpreverse-0.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "6065f3ea7a40f2b7700ca00284368e13", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10845, "upload_time": "2017-02-26T19:20:52", "url": "https://files.pythonhosted.org/packages/2d/89/2b8c34bba21c72ff2dabd3208d5f121f55d3738bc96a70092be6a3adc559/httpreverse-0.4.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cdf2e838508d32fab0ffee0ff15b1af8", "sha256": "f60a3771036379ddc2ae9cd6eca729ac6bf92174b1aed99139d65dd2cf15a6a5" }, "downloads": -1, "filename": "httpreverse-0.4.0.tar.gz", "has_sig": false, "md5_digest": "cdf2e838508d32fab0ffee0ff15b1af8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12563, "upload_time": "2017-02-26T19:20:55", "url": "https://files.pythonhosted.org/packages/72/bd/d0b3339ed3aca93474f8911b67dbf1de74f7662691019ef707b02001e6c0/httpreverse-0.4.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6065f3ea7a40f2b7700ca00284368e13", "sha256": "359097d60bea20746ee7ea1f0b6af1b89936ad5b6263f8f73e6cf9bb5f40dc80" }, "downloads": -1, "filename": "httpreverse-0.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "6065f3ea7a40f2b7700ca00284368e13", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10845, "upload_time": "2017-02-26T19:20:52", "url": "https://files.pythonhosted.org/packages/2d/89/2b8c34bba21c72ff2dabd3208d5f121f55d3738bc96a70092be6a3adc559/httpreverse-0.4.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cdf2e838508d32fab0ffee0ff15b1af8", "sha256": "f60a3771036379ddc2ae9cd6eca729ac6bf92174b1aed99139d65dd2cf15a6a5" }, "downloads": -1, "filename": "httpreverse-0.4.0.tar.gz", "has_sig": false, "md5_digest": "cdf2e838508d32fab0ffee0ff15b1af8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12563, "upload_time": "2017-02-26T19:20:55", "url": "https://files.pythonhosted.org/packages/72/bd/d0b3339ed3aca93474f8911b67dbf1de74f7662691019ef707b02001e6c0/httpreverse-0.4.0.tar.gz" } ] }