diff --git a/mitmproxy2swagger/mitmproxy2swagger.py b/mitmproxy2swagger/mitmproxy2swagger.py index 69232d7..d5a1449 100644 --- a/mitmproxy2swagger/mitmproxy2swagger.py +++ b/mitmproxy2swagger/mitmproxy2swagger.py @@ -300,6 +300,21 @@ def main(override_args: Sequence[str] | None = None): str(status), resp_data_to_set, ) + if ( + "responses" in swagger["paths"][path_template_to_set][method] + and len(swagger["paths"][path_template_to_set][method]["responses"]) + == 0 + ): + # add a default response if there were no responses detected, + # this is for compliance with the OpenAPI spec + content_type = ( + req.get_response_headers().get("content-type") or "text/plain" + ) + + swagger["paths"][path_template_to_set][method]["responses"]["200"] = { + "description": "OK", + "content": {content_type: {}}, + } except FlowReadException as e: print(f"Flow file corrupted: {e}") diff --git a/mitmproxy2swagger/test_openapi_compliance.py b/mitmproxy2swagger/test_openapi_compliance.py new file mode 100644 index 0000000..8803048 --- /dev/null +++ b/mitmproxy2swagger/test_openapi_compliance.py @@ -0,0 +1,55 @@ +from openapi_spec_validator import validate_spec +from mitmproxy2swagger.testing_util import get_nested_key, mitmproxy2swagger_e2e_test + + + +def test_mitmproxy2swagger_compliance_from_mitmproxy_flow_file(): + data = mitmproxy2swagger_e2e_test( + "testdata/test_flows", + "https://httpbin.org/", + [ + "--format", + "flow", + ], + ) + assert data is not None + validate_spec(data) + +def test_mitmproxy2swagger_compliance_from_mitmproxy_flow_file_with_headers(): + data = mitmproxy2swagger_e2e_test( + "testdata/test_flows", + "https://httpbin.org/", + [ + "--format", + "flow", + "--headers", + ], + ) + assert data is not None + validate_spec(data) + +def test_mitmproxy2swagger_compliance_from_har_file_with_headers(): + data = mitmproxy2swagger_e2e_test( + "testdata/sklep.lisek.app.har", + "https://api2.lisek.app/", + [ + "--format", + "har", + "--headers", + ], + ) + assert data is not None + validate_spec(data) + +def test_mitmproxy2swagger_compliance_from_form_data_file_with_headers(): + data = mitmproxy2swagger_e2e_test( + "testdata/form_data_flows", + "https://httpbin.org/", + [ + "--format", + "flow", + "--headers", + ], + ) + assert data is not None + validate_spec(data) diff --git a/poetry.lock b/poetry.lock index 481210b..f06c830 100644 --- a/poetry.lock +++ b/poetry.lock @@ -15,6 +15,25 @@ files = [ [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] +[[package]] +name = "attrs" +version = "22.2.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] + [[package]] name = "black" version = "23.3.0" @@ -760,6 +779,44 @@ files = [ benchmark = ["contexttimer (>=0.3,<0.4)", "json-stream-to-standard-types (>=0.1,<0.2)", "si-prefix (>=1.2,<2)", "tqdm (>=4.64,<5)", "typer (>=0.6,<0.7)"] test = ["json-stream-rs-tokenizer[benchmark]", "pytest (>7.1,<8)"] +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jsonschema-spec" +version = "0.1.4" +description = "JSONSchema Spec with object-oriented paths" +category = "dev" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "jsonschema_spec-0.1.4-py3-none-any.whl", hash = "sha256:34471d8b60e1f06d174236c4d3cf9590fbf3cff1cc733b28d15cd83672bcd062"}, + {file = "jsonschema_spec-0.1.4.tar.gz", hash = "sha256:824c743197bbe2104fcc6dce114a4082bf7f7efdebf16683510cb0ec6d8d53d0"}, +] + +[package.dependencies] +jsonschema = ">=4.0.0,<4.18.0" +pathable = ">=0.4.1,<0.5.0" +PyYAML = ">=5.1" +typing-extensions = ">=4.3.0,<5.0.0" + [[package]] name = "kaitaistruct" version = "0.10" @@ -772,6 +829,52 @@ files = [ {file = "kaitaistruct-0.10.tar.gz", hash = "sha256:a044dee29173d6afbacf27bcac39daf89b654dd418cfa009ab82d9178a9ae52a"}, ] +[[package]] +name = "lazy-object-proxy" +version = "1.9.0" +description = "A fast and thorough lazy object proxy." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, +] + [[package]] name = "ldap3" version = "2.9.1" @@ -1064,6 +1167,47 @@ files = [ [package.dependencies] setuptools = "*" +[[package]] +name = "openapi-schema-validator" +version = "0.4.4" +description = "OpenAPI schema validation for Python" +category = "dev" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "openapi_schema_validator-0.4.4-py3-none-any.whl", hash = "sha256:79f37f38ef9fd5206b924ed7a6f382cea7b649b3b56383c47f1906082b7b9015"}, + {file = "openapi_schema_validator-0.4.4.tar.gz", hash = "sha256:c573e2be2c783abae56c5a1486ab716ca96e09d1c3eab56020d1dc680aa57bf8"}, +] + +[package.dependencies] +jsonschema = ">=4.0.0,<4.18.0" +rfc3339-validator = "*" + +[package.extras] +docs = ["sphinx (>=5.3.0,<6.0.0)", "sphinx-immaterial (>=0.11.0,<0.12.0)"] + +[[package]] +name = "openapi-spec-validator" +version = "0.5.6" +description = "OpenAPI 2.0 (aka Swagger) and OpenAPI 3 spec validator" +category = "dev" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "openapi_spec_validator-0.5.6-py3-none-any.whl", hash = "sha256:4145478f26df16059c147406eaaa59b77ff60e3461ba6edb9aa84d481ed89aaf"}, + {file = "openapi_spec_validator-0.5.6.tar.gz", hash = "sha256:1189d0618ae0678ccf6c883cc1266d381454eece6f21fcf330cc7caea5fc25eb"}, +] + +[package.dependencies] +jsonschema = ">=4.0.0,<4.18.0" +jsonschema-spec = ">=0.1.1,<0.2.0" +lazy-object-proxy = ">=1.7.1,<2.0.0" +openapi-schema-validator = ">=0.4.2,<0.5.0" + +[package.extras] +docs = ["sphinx (>=5.3.0,<6.0.0)", "sphinx-immaterial (>=0.11.0,<0.12.0)"] +requests = ["requests"] + [[package]] name = "packaging" version = "23.1" @@ -1094,6 +1238,18 @@ bcrypt = ["bcrypt (>=3.1.0)"] build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"] totp = ["cryptography"] +[[package]] +name = "pathable" +version = "0.4.3" +description = "Object-oriented paths" +category = "dev" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "pathable-0.4.3-py3-none-any.whl", hash = "sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14"}, + {file = "pathable-0.4.3.tar.gz", hash = "sha256:5c869d315be50776cc8a993f3af43e0c60dc01506b399643f919034ebf4cdcab"}, +] + [[package]] name = "pathspec" version = "0.11.1" @@ -1301,6 +1457,43 @@ files = [ {file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"}, ] +[[package]] +name = "pyrsistent" +version = "0.19.3" +description = "Persistent/Functional/Immutable data structures" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, + {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, + {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, + {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, + {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, + {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, +] + [[package]] name = "pytest" version = "7.3.1" @@ -1393,6 +1586,21 @@ files = [ {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +description = "A pure python RFC3339 validator" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, + {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, +] + +[package.dependencies] +six = "*" + [[package]] name = "ruamel-yaml" version = "0.17.21" @@ -1475,6 +1683,18 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-g testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "sortedcontainers" version = "2.4.0" @@ -1705,4 +1925,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "16321978e1b36e7b409d066c388ddb3a553bad6a6d18dbdf8ee9e5f8ec761545" +content-hash = "01b0473616bfe50f90f7faa4baff4ba915ab3db9dce01af72181359bc92efb7d" diff --git a/pyproject.toml b/pyproject.toml index 5e77466..8892c9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ mitmproxy = "^9.0.1" "ruamel.yaml" = "^0.17.21" json-stream = "^2.2.0" + [tool.poetry.group.dev.dependencies] black = "^23.1.0" isort = "^5.12.0" @@ -23,6 +24,7 @@ pre-commit = "^3.1.1" pytest = "^7.2.1" pytest-asyncio = "^0.20.3" vermin = "^1.5.1" +openapi-spec-validator = "^0.5.6" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/specs.yml b/specs.yml new file mode 100644 index 0000000..00496f3 --- /dev/null +++ b/specs.yml @@ -0,0 +1,1052 @@ +openapi: 3.0.0 +info: + title: testdata/sklep.lisek.app.har Mitmproxy2Swagger + version: 1.0.0 +servers: +- url: https://sklep.lisek.app + description: The default server +- url: https://api2.lisek.app + description: The default server +paths: + /api/inventory/image: + get: + summary: GET image + responses: {} + parameters: + - name: url + in: query + required: false + schema: + type: string + - name: width + in: query + required: false + schema: + type: number + - name: height + in: query + required: false + schema: + type: number + - name: noEnlarge + in: query + required: false + schema: + type: string + /api/settings/countries: + get: + summary: GET countries + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: array + items: + type: object + properties: + countryCode: + type: string + areaCode: + type: string + maxPhoneLength: + type: number + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS countries + responses: {} + /api/darkstores: + get: + summary: GET darkstores + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: array + items: + type: object + properties: + id: + type: number + name: + type: string + identifier: + type: string + virtual: + type: boolean + address: + type: object + properties: + id: + type: number + street: + type: string + streetNo: + type: string + city: + type: string + doorNo: + type: string + floor: + type: string + latitude: + type: number + longitude: + type: number + postalCode: + type: string + porch: + type: string + comments: + type: string + courierComment: + type: string + isCurrent: + type: boolean + isCurrentNotVirtual: + type: boolean + label: + type: object + isVirtual: + type: boolean + darkstoreId: + type: number + fullInfo: + type: object + area: + type: array + items: + type: string + openingTime: + type: object + properties: + openHour: + type: number + openMinute: + type: number + closeHour: + type: number + closeMinute: + type: number + localTime: + type: boolean + courierSplitEnabled: + type: boolean + sortKey: + type: number + sliderId: + type: number + available: + type: boolean + unavailableMessage: + type: string + unavailableFromTime: + type: object + unavailableUntilTime: + type: string + orderStackingEnabled: + type: string + darkstoreRuntimeSettigs: + type: object + properties: + pickerStackRemovePermission: + type: string + isModifyStackSortOrderAllowed: + type: boolean + isAddressVisibleForCourier: + type: object + transportTypeForCouriers: + type: object + alcoholReceiptPrintingGroup: + type: object + properties: + groupName: + type: string + pharmacyReceiptPrintingGroup: + type: object + properties: + groupName: + type: string + additionalBasketFees: + type: object + properties: + longDistanceFeeMinDistanceInM: + type: object + longDistanceFee: + type: object + isVirtualSplitEnabled: + type: boolean + additionalInfoText: + type: object + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS darkstores + responses: {} + /api/darkstores/default: + get: + summary: GET default + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: object + properties: + id: + type: number + name: + type: string + identifier: + type: string + virtual: + type: boolean + address: + type: object + properties: + id: + type: number + street: + type: string + streetNo: + type: string + city: + type: string + doorNo: + type: string + floor: + type: string + latitude: + type: number + longitude: + type: number + postalCode: + type: string + porch: + type: string + comments: + type: string + courierComment: + type: object + isCurrent: + type: boolean + isCurrentNotVirtual: + type: boolean + label: + type: object + isVirtual: + type: boolean + darkstoreId: + type: number + fullInfo: + type: object + area: + type: array + items: + type: string + openingTime: + type: object + properties: + openHour: + type: number + openMinute: + type: number + closeHour: + type: number + closeMinute: + type: number + localTime: + type: boolean + courierSplitEnabled: + type: boolean + sortKey: + type: number + sliderId: + type: number + available: + type: boolean + unavailableMessage: + type: string + unavailableFromTime: + type: object + unavailableUntilTime: + type: string + orderStackingEnabled: + type: string + darkstoreRuntimeSettigs: + type: object + properties: + pickerStackRemovePermission: + type: string + isModifyStackSortOrderAllowed: + type: boolean + isAddressVisibleForCourier: + type: boolean + transportTypeForCouriers: + type: object + alcoholReceiptPrintingGroup: + type: object + properties: + groupName: + type: string + pharmacyReceiptPrintingGroup: + type: object + properties: + groupName: + type: string + additionalBasketFees: + type: object + properties: + longDistanceFeeMinDistanceInM: + type: object + longDistanceFee: + type: object + isVirtualSplitEnabled: + type: boolean + additionalInfoText: + type: object + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS default + responses: {} + /api/users/anonymous: + post: + summary: POST anonymous + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: object + properties: + token: + type: string + refreshToken: + type: string + expires: + type: number + user: + type: object + properties: + id: + type: number + email: + type: string + firstname: + type: string + lastname: + type: string + phone: + type: string + newsletter: + type: boolean + newsletterEmail: + type: boolean + newsletterPhone: + type: boolean + sendInfo: + type: boolean + roles: + type: array + items: + type: string + franchises: + type: object + anonymousClientSource: + type: string + registerClientSource: + type: string + anonymous: + type: boolean + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + requestBody: + content: + application/json: + schema: + type: string + options: + summary: OPTIONS anonymous + responses: {} + /api/inventory/settings: + get: + summary: GET settings + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: object + properties: + storageBaseUrl: + type: string + productBaseUrl: + type: string + imageTransformationUrl: + type: string + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS settings + responses: {} + /api/transactionsettings: + get: + summary: GET transactionsettings + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: object + properties: + discountThreshold: + type: number + maxWeight: + type: number + minOrder: + type: number + freeShippingThreshold: + type: number + deliveryCost: + type: number + discountPercent: + type: number + maxStackSize: + type: number + maxStackTimeInMins: + type: number + startStackFromClosest: + type: boolean + paperBagEan: + type: string + paperBagFee: + type: number + paperBagPerXItems: + type: number + paperBagPerXPrice: + type: object + paperBagInfiniteStock: + type: boolean + alcoholCategories: + type: string + verifyAgeCategories: + type: string + excludeFromAlcoholAndVerifyAgeEans: + type: string + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS transactionsettings + responses: {} + /api/users/profile: + get: + summary: GET profile + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: object + properties: + firstName: + type: string + lastName: + type: string + phone: + type: string + dateOfBirth: + type: object + email: + type: string + newsletter: + type: boolean + sendInfo: + type: boolean + smsCode: + type: object + newsletterPhone: + type: boolean + newsletterEmail: + type: boolean + anonymous: + type: boolean + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS profile + responses: {} + /api/deliveryfees: + get: + summary: GET deliveryfees + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: array + items: + type: object + properties: + sortKey: + type: number + valueFrom: + type: number + valueTo: + type: number + fee: + type: number + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS deliveryfees + responses: {} + /api/inventory/promo/5/full: + get: + summary: GET full + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: array + items: + type: object + properties: + product: + type: object + properties: + ean: + type: string + headline: + type: string + title: + type: string + subTitle: + type: string + price: + type: number + vat: + type: number + isAlcohol: + type: boolean + verifyAge: + type: boolean + maxQuantity: + type: number + packInfo: + type: string + mainCategoryId: + type: number + subCategoryId: + type: number + imagePath: + type: string + imagePathLastComponent: + type: string + isHit: + type: boolean + isPromo: + type: boolean + isNew: + type: boolean + isCold: + type: boolean + isBundle: + type: boolean + dimensions: + type: string + information: + type: string + ingredients: + type: string + dietary: + type: string + storage: + type: string + misk: + type: string + contact: + type: object + weight: + type: string + nutrition: + type: string + pickInfo: + type: object + properties: + shelf: + type: number + level: + type: number + levelColor: + type: object + path: + type: number + quantity: + type: number + supplier: + type: string + isThermalBagRequired: + type: boolean + priceBeforePromo: + type: object + promoText: + type: string + packUnit: + type: string + packAmount: + type: number + pricePerUnitText: + type: string + id: + type: number + ean: + type: string + type: + type: string + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS full + responses: {} + /api/sliders/5: + get: + summary: GET 5 + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: array + items: + type: object + properties: + link: + type: string + type: + type: number + position: + type: number + categoryId: + type: object + mainCategoryId: + type: object + containsAlco: + type: boolean + redirectUrl: + type: object + webRedirectUrl: + type: object + redirectable: + type: boolean + isExternalUrl: + type: boolean + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS 5 + responses: {} + /api/inventory/5/categories/31/products: + get: + summary: GET products + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: object + properties: + id: + type: number + name: + type: string + key: + type: string + number: + type: number + order: + type: number + imagePath: + type: string + iconPath: + type: string + mobileCategoryView: + type: object + mobileMainCategoryView: + type: object + webCategoryMainView: + type: object + parentId: + type: object + alcohol: + type: boolean + verifyAge: + type: boolean + backgroundColor: + type: string + subCategories: + type: array + items: {} + products: + type: array + items: + type: object + properties: + id: + type: number + quantity: + type: number + ean: + type: string + maxQuantity: + type: number + headline: + type: string + title: + type: string + subTitle: + type: string + price: + type: number + packInfo: + type: string + imagePath: + type: string + isHit: + type: boolean + isPromo: + type: boolean + priceBeforePromo: + type: object + isAlcohol: + type: boolean + verifyAge: + type: boolean + promoText: + type: string + isNew: + type: boolean + isCold: + type: boolean + showOutOfStock: + type: boolean + weight: + type: string + vat: + type: number + categoryId: + type: number + subCategoryId: + type: number + subCategoryName: + type: string + stockItemId: + type: number + isBundle: + type: boolean + bundleId: + type: object + packUnit: + type: string + packAmount: + type: number + pricePerUnitText: + type: object + sortOrder: + type: number + isSubCategorySorted: + type: boolean + stockItemId: + type: number + categorySeo: + type: object + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS products + responses: {} + /api/inventory/5/categories/73/products: + get: + summary: GET products + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: object + properties: + id: + type: number + name: + type: string + key: + type: string + number: + type: number + order: + type: number + imagePath: + type: string + iconPath: + type: string + mobileCategoryView: + type: object + mobileMainCategoryView: + type: object + webCategoryMainView: + type: object + parentId: + type: object + alcohol: + type: boolean + verifyAge: + type: boolean + backgroundColor: + type: string + subCategories: + type: array + items: {} + products: + type: array + items: + type: object + properties: + id: + type: number + quantity: + type: number + ean: + type: string + maxQuantity: + type: number + headline: + type: string + title: + type: string + subTitle: + type: string + price: + type: number + packInfo: + type: string + imagePath: + type: string + isHit: + type: boolean + isPromo: + type: boolean + priceBeforePromo: + type: object + isAlcohol: + type: boolean + verifyAge: + type: boolean + promoText: + type: string + isNew: + type: boolean + isCold: + type: boolean + showOutOfStock: + type: boolean + weight: + type: string + vat: + type: number + categoryId: + type: number + subCategoryId: + type: number + subCategoryName: + type: string + stockItemId: + type: number + isBundle: + type: boolean + bundleId: + type: object + packUnit: + type: string + packAmount: + type: number + pricePerUnitText: + type: object + sortOrder: + type: number + isSubCategorySorted: + type: boolean + stockItemId: + type: number + categorySeo: + type: object + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS products + responses: {} + /api/inventory/5/stocks: + get: + summary: GET stocks + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + value: + type: array + items: + type: object + properties: + e: + type: string + q: + type: number + success: + type: boolean + errors: + type: array + items: {} + errorTexts: + type: string + options: + summary: OPTIONS stocks + responses: {} +x-path-templates: +# Remove the ignore: prefix to generate an endpoint with its URL +# Lines that are closer to the top take precedence, the matching is greedy +- ignore:/ +- ignore:/favicon.svg +- ignore:/serverBuild/assets/LisekFooter.png +- ignore:/serverBuild/assets/LogoText.png +- ignore:/serverBuild/assets/apple.png +- ignore:/serverBuild/assets/basicInformationsDays.jpg +- ignore:/serverBuild/assets/basicInformationsDelivery.jpg +- ignore:/serverBuild/assets/basicInformationsHours.jpg +- ignore:/serverBuild/assets/hand.png +- ignore:/serverBuild/assets/leftHand.png +- ignore:/serverBuild/assets/lisek_logo.png +- ignore:/serverBuild/assets/logoOrange.png +- ignore:/serverBuild/assets/logoWhite.png +- ignore:/static/css/main.7ef204a2.css +- ignore:/static/js/main.ca596ece.js +- ignore:/static/media/hand.9bb7bbeaf6bcf0bbf6ce.png +- ignore:/static/media/leftHand.e9bb7969d7df1ed82595.png +- ignore:/static/media/lisek_logo.3dc197ddbb239a0876c3.png +- /api/inventory/{id}/categories/{id1}/products +- /api/inventory/{id}/stocks +- /api/inventory/promo/{id}/full +- /api/sliders/{id}