From 4f91af1a3f4db2d8c87606a51c20d8d208c68049 Mon Sep 17 00:00:00 2001 From: Jake Nielsen Date: Tue, 28 Jul 2020 16:31:38 -0700 Subject: [PATCH] Adds some simple sanity unit-tests and related bugfixes --- .gitignore | 1 + Pipfile | 17 + Pipfile.lock | 484 +++++++++++++++ dialpad/resources/contact.py | 4 - .../resources/{deparment.py => department.py} | 0 dialpad/resources/event_subscription.py | 2 +- dialpad/resources/stats.py | 4 +- dialpad/resources/user.py | 2 +- test/__init__.py | 3 + test/test_resource_sanity.py | 563 ++++++++++++++++++ test/utils.py | 54 ++ 11 files changed, 1126 insertions(+), 8 deletions(-) create mode 100644 Pipfile create mode 100644 Pipfile.lock rename dialpad/resources/{deparment.py => department.py} (100%) create mode 100644 test/__init__.py create mode 100644 test/test_resource_sanity.py create mode 100644 test/utils.py diff --git a/.gitignore b/.gitignore index 37fc9d4..81d5e49 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,4 @@ ENV/ # Rope project settings .ropeproject +test/.resources/ diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..760fec6 --- /dev/null +++ b/Pipfile @@ -0,0 +1,17 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] +nose2 = "*" +openapi-core = "==0.12.0" +swagger-parser = "*" +swagger-stub = "*" + +[packages] +requests = "*" +cached-property = "*" + +[requires] +python_version = "2.7" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..f0954a8 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,484 @@ +{ + "_meta": { + "hash": { + "sha256": "9f6beefcf68092761946f4293f20d28045d3a9cff93f2de4cc3e018fc77899c9" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "2.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "cached-property": { + "hashes": [ + "sha256:3a026f1a54135677e7da5ce819b0c690f156f37976f3e30c5430740725203d7f", + "sha256:9217a59f14a5682da7c4b8829deadbfc194ac22e9908ccf7c8820234e80a1504" + ], + "index": "pypi", + "version": "==1.5.1" + }, + "certifi": { + "hashes": [ + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + ], + "version": "==2020.6.20" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "idna": { + "hashes": [ + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "version": "==2.10" + }, + "requests": { + "hashes": [ + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + ], + "index": "pypi", + "version": "==2.24.0" + }, + "urllib3": { + "hashes": [ + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + ], + "version": "==1.25.10" + } + }, + "develop": { + "atomicwrites": { + "hashes": [ + "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197", + "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a" + ], + "version": "==1.4.0" + }, + "attrs": { + "hashes": [ + "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", + "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + ], + "version": "==19.3.0" + }, + "backports.functools-lru-cache": { + "hashes": [ + "sha256:0bada4c2f8a43d533e4ecb7a12214d9420e66eb206d54bf2d682581ca4b80848", + "sha256:8fde5f188da2d593bd5bc0be98d9abc46c95bb8a9dde93429570192ee6cc2d4a" + ], + "markers": "python_version < '3.2'", + "version": "==1.6.1" + }, + "backports.functools-partialmethod": { + "hashes": [ + "sha256:d769b58383a3d843d15d49f57fa7f01efd2941ec277d857988f7a9c302b85669", + "sha256:d9ac43563f1be8bced18b7b526e38312872e81328a06221e0e96b1c37aa5fd2e" + ], + "version": "==3.5.1.0" + }, + "certifi": { + "hashes": [ + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + ], + "version": "==2020.6.20" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "configparser": { + "hashes": [ + "sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c", + "sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df" + ], + "markers": "python_version < '3'", + "version": "==4.0.2" + }, + "contextlib2": { + "hashes": [ + "sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e", + "sha256:3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b" + ], + "markers": "python_version < '3'", + "version": "==0.6.0.post1" + }, + "coverage": { + "hashes": [ + "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb", + "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3", + "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716", + "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034", + "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3", + "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8", + "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0", + "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f", + "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4", + "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962", + "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d", + "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b", + "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4", + "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3", + "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258", + "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59", + "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01", + "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd", + "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b", + "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d", + "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89", + "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd", + "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b", + "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d", + "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46", + "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546", + "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082", + "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b", + "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4", + "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8", + "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811", + "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd", + "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651", + "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0" + ], + "version": "==5.2.1" + }, + "enum34": { + "hashes": [ + "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53", + "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328", + "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248" + ], + "version": "==1.1.10" + }, + "funcsigs": { + "hashes": [ + "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca", + "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50" + ], + "markers": "python_version < '3.0'", + "version": "==1.0.2" + }, + "functools32": { + "hashes": [ + "sha256:89d824aa6c358c421a234d7f9ee0bd75933a67c29588ce50aaa3acdf4d403fa0", + "sha256:f6253dfbe0538ad2e387bd8fdfd9293c925d63553f5813c4e587745416501e6d" + ], + "markers": "python_version < '3'", + "version": "==3.2.3.post2" + }, + "httpretty": { + "hashes": [ + "sha256:66216f26b9d2c52e81808f3e674a6fb65d4bf719721394a1a9be926177e55fbe" + ], + "version": "==0.9.7" + }, + "idna": { + "hashes": [ + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "version": "==2.10" + }, + "importlib-metadata": { + "hashes": [ + "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83", + "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070" + ], + "markers": "python_version < '3.8'", + "version": "==1.7.0" + }, + "jinja2": { + "hashes": [ + "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", + "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" + ], + "version": "==2.11.2" + }, + "jsonschema": { + "hashes": [ + "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163", + "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a" + ], + "version": "==3.2.0" + }, + "lazy-object-proxy": { + "hashes": [ + "sha256:00b78a97a79d0dfefa584d44dd1aba9668d3de7ec82335ba0ff51d53ef107143", + "sha256:042b54fd71c2092e6d10e5e66fa60f65c5954f8145e809f5d9f394c9b13d32ee", + "sha256:11f87dc06eb5f376cc6d5f0c19a1b4dca202035622777c4ce8e5b72c87b035d6", + "sha256:19ae6f6511a02008ef3554e158c41bb2a8e5c8455935b98d6da076d9f152fd7c", + "sha256:22c1935c6f8e3d6ea2e169eb03928adbdb8a2251d2890f8689368d65e70aa176", + "sha256:30ef2068f4f94660144515380ef04b93d15add2214eab8be4cd46ebc900d681c", + "sha256:33da47ba3a581860ddd3d38c950a5fe950ca389f7123edd0d6ab0bc473499fe7", + "sha256:3e8698dc384857413580012f4ca322d89e63ef20fc3d4635a5b606d6d4b61f6a", + "sha256:4fdd7113fc5143c72dacf415079eec42fcbe69cc9d3d291b4ca742e3a9455807", + "sha256:63b6d9a5077d54db271fcc6772440f7380ec3fa559d0e2497dbfae2f47c2c814", + "sha256:8133b63b05f12751cddd8e3e7f02ba39dc7cfa7d2ba99d80d7436f0ba26d6b75", + "sha256:89b8e5780e49753e2b4cd5aab45d3df092ddcbba3de2c4d4492a029588fe1758", + "sha256:8d82e27cbbea6edb8821751806f39f5dcfd7b46a5e23d27b98d6d8c8ec751df8", + "sha256:92cedd6e26712505adb1c17fab64651a498cc0102a80ba562ff4a2451088f57a", + "sha256:9723364577b79ad9958a68851fe2acb94da6fd25170c595516a8289e6a129043", + "sha256:c484020ad26973a14a7cb1e1d2e0bfe97cf6803273ae9bd154e0213cc74bad49", + "sha256:c697bd1b333b3e6abdff04ef9f5fb4b1936633d9cc4e28d90606705c9083254c", + "sha256:d0f7e14ff3424639d33e6bc449e77e4b345e52c21bbd6f6004a1d219196e2664", + "sha256:db2df3eff7ed3e6813638686f1bb5934d1a0662d9d3b4196b5164a86be3a1e8f", + "sha256:edbcb4c5efabd93ede05b272296a5a78a67e9b6e82ba7f51a07b8103db06ce01", + "sha256:ef355fb3802e0fc5a71dadb65a3c317bfc9bdf567d357f8e0b1900b432ffe486", + "sha256:fe2f61fed5817bf8db01d9a72309ed5990c478a077e9585b58740c26774bce39" + ], + "version": "==1.5.1" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + ], + "version": "==1.1.1" + }, + "mock": { + "hashes": [ + "sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1", + "sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba" + ], + "markers": "python_version < '3.6'", + "version": "==2.0.0" + }, + "more-itertools": { + "hashes": [ + "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", + "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc", + "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9" + ], + "markers": "python_version <= '2.7'", + "version": "==5.0.0" + }, + "nose2": { + "hashes": [ + "sha256:8762f77925bbafcdf38331e0e2ee718756fb75ff74b1f9097cd08731ad59ab5e", + "sha256:fd4b84c65ecea869080a23bdb8916716f5363df3b899933991c861ada8aa3f48" + ], + "index": "pypi", + "version": "==0.9.2" + }, + "openapi-core": { + "hashes": [ + "sha256:8522a49feb90ba16efcb670af36893f1dbe17b500e6bec480ae7412635ac607a", + "sha256:8dc176472b5fcf9f0e6906b67498f2d1dc4d1719bc4726e7a77d958c26a590ab", + "sha256:b30b425ca3a01282e66784c820a5196118ab16092d20bf6ade171f8aedcb357b" + ], + "index": "pypi", + "version": "==0.12.0" + }, + "openapi-spec-validator": { + "hashes": [ + "sha256:6dd75e50c94f1bb454d0e374a56418e7e06a07affb2c7f1df88564c5d728dac3", + "sha256:79381a69b33423ee400ae1624a461dae7725e450e2e306e32f2dd8d16a4d85cb", + "sha256:ec1b01a00e20955a527358886991ae34b4b791b253027ee9f7df5f84b59d91c7" + ], + "version": "==0.2.9" + }, + "packaging": { + "hashes": [ + "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", + "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" + ], + "version": "==20.4" + }, + "pathlib2": { + "hashes": [ + "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db", + "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868" + ], + "markers": "python_version < '3.6'", + "version": "==2.3.5" + }, + "pbr": { + "hashes": [ + "sha256:07f558fece33b05caf857474a366dfcc00562bca13dd8b47b2b3e22d9f9bf55c", + "sha256:579170e23f8e0c2f24b0de612f71f648eccb79fb1322c814ae6b3c07b5ba23e8" + ], + "version": "==5.4.5" + }, + "pluggy": { + "hashes": [ + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + ], + "version": "==0.13.1" + }, + "py": { + "hashes": [ + "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2", + "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342" + ], + "version": "==1.9.0" + }, + "pyparsing": { + "hashes": [ + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + ], + "version": "==2.4.7" + }, + "pyrsistent": { + "hashes": [ + "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3" + ], + "version": "==0.16.0" + }, + "pytest": { + "hashes": [ + "sha256:50fa82392f2120cc3ec2ca0a75ee615be4c479e66669789771f1758332be4353", + "sha256:a00a7d79cbbdfa9d21e7d0298392a8dd4123316bfac545075e6f8f24c94d8c97" + ], + "version": "==4.6.11" + }, + "pyyaml": { + "hashes": [ + "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", + "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", + "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", + "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", + "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", + "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", + "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", + "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", + "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", + "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", + "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" + ], + "version": "==5.3.1" + }, + "requests": { + "hashes": [ + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + ], + "index": "pypi", + "version": "==2.24.0" + }, + "scandir": { + "hashes": [ + "sha256:2586c94e907d99617887daed6c1d102b5ca28f1085f90446554abf1faf73123e", + "sha256:2ae41f43797ca0c11591c0c35f2f5875fa99f8797cb1a1fd440497ec0ae4b022", + "sha256:2b8e3888b11abb2217a32af0766bc06b65cc4a928d8727828ee68af5a967fa6f", + "sha256:2c712840c2e2ee8dfaf36034080108d30060d759c7b73a01a52251cc8989f11f", + "sha256:4d4631f6062e658e9007ab3149a9b914f3548cb38bfb021c64f39a025ce578ae", + "sha256:67f15b6f83e6507fdc6fca22fedf6ef8b334b399ca27c6b568cbfaa82a364173", + "sha256:7d2d7a06a252764061a020407b997dd036f7bd6a175a5ba2b345f0a357f0b3f4", + "sha256:8c5922863e44ffc00c5c693190648daa6d15e7c1207ed02d6f46a8dcc2869d32", + "sha256:92c85ac42f41ffdc35b6da57ed991575bdbe69db895507af88b9f499b701c188", + "sha256:b24086f2375c4a094a6b51e78b4cf7ca16c721dcee2eddd7aa6494b42d6d519d", + "sha256:cb925555f43060a1745d0a321cca94bcea927c50114b623d73179189a4e100ac" + ], + "markers": "python_version < '3.5'", + "version": "==1.10.0" + }, + "six": { + "hashes": [ + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + ], + "version": "==1.15.0" + }, + "strict-rfc3339": { + "hashes": [ + "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277" + ], + "version": "==0.7" + }, + "swagger-parser": { + "hashes": [ + "sha256:0802ea49468fab102ed8cb8a6cc6c41423ad60fd396c14a2f187c7d5165f2e18", + "sha256:8c0263e3128d3af9150c6fcd83d768967211f3bcb4da330fbe318fb44e38bdd2" + ], + "index": "pypi", + "version": "==1.0.1" + }, + "swagger-spec-validator": { + "hashes": [ + "sha256:d1514ec7e3c058c701f27cc74f85ceb876d6418c9db57786b9c54085ed5e29eb", + "sha256:f4f23ee4dbd52bfcde90b1144dde22304add6260e9f29252e9fd7814c9b8fd16" + ], + "version": "==2.7.3" + }, + "swagger-stub": { + "hashes": [ + "sha256:6ff47e489e183a5f981de9554228750a738cd18aaf2c7b140bff3680a0317db1", + "sha256:84eb254ccf94f6adb67a3a658e7f3f5a2d5007b3e32784b9b6461cde54c36c23" + ], + "index": "pypi", + "version": "==0.2.1" + }, + "urllib3": { + "hashes": [ + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + ], + "version": "==1.25.10" + }, + "wcwidth": { + "hashes": [ + "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", + "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" + ], + "version": "==0.2.5" + }, + "zipp": { + "hashes": [ + "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1", + "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921" + ], + "version": "==1.2.0" + } + } +} diff --git a/dialpad/resources/contact.py b/dialpad/resources/contact.py index cb2a063..197ba0e 100644 --- a/dialpad/resources/contact.py +++ b/dialpad/resources/contact.py @@ -52,8 +52,6 @@ def create_with_uid(self, first_name, last_name, uid, **kwargs): emails (list, optional): A list of email addresses associated with the contact. extension (str, optional): The contact's extension number. job_title (str, optional): The contact's job title. - owner_id (str, optional): The ID of the user who should own this contact. If no owner_id is - specified, then a company-level shared contact will be created. phones (list, optional): A list of e164 numbers that belong to this contact. trunk_group (str, optional): The contact's trunk group. urls (list, optional): A list of urls that pertain to this contact. @@ -97,8 +95,6 @@ def patch(self, contact_id, **kwargs): emails (list, optional): A list of email addresses associated with the contact. extension (str, optional): The contact's extension number. job_title (str, optional): The contact's job title. - owner_id (str, optional): The ID of the user who should own this contact. If no owner_id is - specified, then a company-level shared contact will be created. phones (list, optional): A list of e164 numbers that belong to this contact. trunk_group (str, optional): The contact's trunk group. urls (list, optional): A list of urls that pertain to this contact. diff --git a/dialpad/resources/deparment.py b/dialpad/resources/department.py similarity index 100% rename from dialpad/resources/deparment.py rename to dialpad/resources/department.py diff --git a/dialpad/resources/event_subscription.py b/dialpad/resources/event_subscription.py index b402b71..4b83f49 100644 --- a/dialpad/resources/event_subscription.py +++ b/dialpad/resources/event_subscription.py @@ -129,7 +129,7 @@ def put_sms_event_subscription(self, subscription_id, url, direction, enabled=Tr """ return self.request(['sms', subscription_id], method='PUT', - data=dict(url=url, enabled=enabled, group_calls_only=group_calls_only, + data=dict(url=url, enabled=enabled, direction=direction, **kwargs)) def delete_sms_event_subscription(self, subscription_id): diff --git a/dialpad/resources/stats.py b/dialpad/resources/stats.py index f58d9fd..dec3bcd 100644 --- a/dialpad/resources/stats.py +++ b/dialpad/resources/stats.py @@ -40,8 +40,8 @@ def post(self, coaching_group=False, days_ago_start=1, days_ago_end=30, is_today data = { 'coaching_group': coaching_group, - 'days_ago_start': days_ago_start, - 'days_ago_end': days_ago_end, + 'days_ago_start': str(days_ago_start), + 'days_ago_end': str(days_ago_end), 'is_today': is_today, 'export_type': export_type, 'stat_type': stat_type, diff --git a/dialpad/resources/user.py b/dialpad/resources/user.py index e60cfe7..cae8019 100644 --- a/dialpad/resources/user.py +++ b/dialpad/resources/user.py @@ -105,7 +105,7 @@ def toggle_call_recording(self, user_id, **kwargs): See Also: https://developers.dialpad.com/reference#callapi_updateactivecall """ - return self.request([user_id, 'activecall'], data=kwargs) + return self.request([user_id, 'activecall'], method='PATCH', data=kwargs) def assign_number(self, user_id, **kwargs): """Assigns a new number to the user. diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..a15b89c --- /dev/null +++ b/test/__init__.py @@ -0,0 +1,3 @@ +import utils + +utils.prepare_test_resources() diff --git a/test/test_resource_sanity.py b/test/test_resource_sanity.py new file mode 100644 index 0000000..a3c57e9 --- /dev/null +++ b/test/test_resource_sanity.py @@ -0,0 +1,563 @@ +#!/usr/bin/env python + +"""Tests to automatically detect common issues with resource definitions. + +In particular these tests will look through the files in dialpad-python-sdk/dialpad/resources/ and +ensure: + +- All subclasses of DialpadResource are exposed directly in resources/__init__.py +- All resources are available as properties of DialpadClient +- Public methods defined on the concrete subclasses only make web requests that agree with + the Dialpad API's open-api spec +""" + +# TODO: Clean this up +import inspect +import pkgutil +import pytest +import requests +import unittest +import utils + +from swagger_stub import swagger_stub +from swagger_parser import SwaggerParser + +from dialpad.client import DialpadClient +from dialpad import resources +from dialpad.resources.resource import DialpadResource + + +import json + + +# The "swagger_files_url" pytest fixture stubs out live requests with a schema validation check +# against the Dialpad API swagger spec. + +# NOTE: Responses returned by the stub will not necessarily be a convincing dummy for the responses +# returned by the live API, so some complex scenarios may not be possible to test using this +# strategy. +@pytest.fixture(scope='module') +def swagger_files_url(): + return [ + (utils.resource_filepath('swagger_spec.json'), 'https://dialpad.com'), + ] + + +class TestResourceSanity: + """Sanity-tests for (largely) automatically validating new and existing client API methods. + + When new API resource methods are added to the library, examples of each method must be added to + EX_METHOD_CALLS to allow the unit tests to call those methods and validate that the API requests + they generate adhere to the swagger spec. + + The example calls should generally include as many keyword arguments as possible so that any + potential mistakes in the parameter names, url path, and request body can be caught by the + schema tests. + + Entries in the "EX_METHOD_CALLS" dictionary should be of the form: + { + '': { + 'method_name': { + 'arg_name': arg_value, + 'other_arg_name': other_arg_value, + }, + 'other_method_name': etc... + } + } + """ + + EX_METHOD_CALLS = { + 'BlockedNumberResource': { + 'list': {}, + 'block_numbers': { + 'numbers': ['+12223334444'] + }, + 'unblock_numbers': { + 'numbers': ['+12223334444'] + }, + 'get': { + 'number': '+12223334444' + }, + }, + 'CallResource': { + 'initiate_call': { + 'phone_number': '+12223334444', + 'user_id': '123', + 'group_id': '123', + 'group_type': 'department', + 'device_id': '123', + }, + }, + 'CallRouterResource': { + 'get': { + 'call_router_id': '123', + }, + 'post': { + 'name': 'Test Router', + 'routing_url': 'fakeurl.com/url', + 'office_id': '123', + 'default_target_id': '123', + 'default_target_type': 'user', + 'enabled': True, + 'secret': '123', + }, + 'patch': { + 'call_router_id': '123', + 'name': 'Test Router', + 'routing_url': 'fakeurl.com/url', + 'office_id': '123', + 'default_target_id': '123', + 'default_target_type': 'user', + 'enabled': True, + 'secret': '123', + }, + 'delete': { + 'call_router_id': '123', + }, + 'assign_number': { + 'call_router_id': '123', + 'area_code': '519', + }, + }, + 'CallbackResource': { + 'enqueue_callback': { + 'call_center_id': '123', + 'phone_number': '+12223334444', + }, + }, + 'CallCenterResource': { + 'get': { + 'call_center_id': '123', + }, + 'get_operators': { + 'call_center_id': '123', + }, + }, + 'CompanyResource': { + 'get': {}, + }, + 'ContactResource': { + 'list': { + 'owner_id': '123', + }, + 'create': { + 'first_name': 'Testiel', + 'last_name': 'McTestersen', + 'company_name': 'ABC', + 'emails': ['tmtesten@test.com'], + 'extension': '123', + 'job_title': 'Eric the half-a-bee', + 'owner_id': '123', + 'phones': ['+12223334444'], + 'trunk_group': '123', + 'urls': ['test.com/about'], + }, + 'create_with_uid': { + 'first_name': 'Testiel', + 'last_name': 'McTestersen', + 'uid': 'UUID-updownupdownleftrightab', + 'company_name': 'ABC', + 'emails': ['tmtesten@test.com'], + 'extension': '123', + 'job_title': 'Eric the half-a-bee', + 'phones': ['+12223334444'], + 'trunk_group': '123', + 'urls': ['test.com/about'], + }, + 'delete': { + 'contact_id': '123', + }, + 'get': { + 'contact_id': '123', + }, + 'patch': { + 'contact_id': '123', + 'first_name': 'Testiel', + 'last_name': 'McTestersen', + 'company_name': 'ABC', + 'emails': ['tmtesten@test.com'], + 'extension': '123', + 'job_title': 'Eric the half-a-bee', + 'phones': ['+12223334444'], + 'trunk_group': '123', + 'urls': ['test.com/about'], + }, + }, + 'DepartmentResource': { + 'get': { + 'department_id': '123', + }, + 'get_operators': { + 'department_id': '123', + }, + }, + 'EventSubscriptionResource': { + 'list_call_event_subscriptions': { + 'target_id': '123', + 'target_type': 'room', + }, + 'get_call_event_subscription': { + 'subscription_id': '123', + }, + 'put_call_event_subscription': { + 'subscription_id': '123', + 'url': 'test.com/subhook', + 'secret': 'badsecret', + 'enabled': True, + 'group_calls_only': False, + 'target_id': '123', + 'target_type': 'office', + 'call_states': ['connected', 'queued'], + }, + 'delete_call_event_subscription': { + 'subscription_id': '123', + }, + 'list_sms_event_subscriptions': { + 'target_id': '123', + 'target_type': 'room', + }, + 'get_sms_event_subscription': { + 'subscription_id': '123', + }, + 'put_sms_event_subscription': { + 'subscription_id': '123', + 'url': 'test.com/subhook', + 'secret': 'badsecret', + 'direction': 'outbound', + 'enabled': True, + 'target_id': '123', + 'target_type': 'office', + }, + 'delete_sms_event_subscription': { + 'subscription_id': '123', + }, + }, + 'NumberResource': { + 'list': { + 'status': 'available', + }, + 'get': { + 'number': '+12223334444', + }, + 'unassign': { + 'number': '+12223334444', + }, + 'assign': { + 'number': '+12223334444', + 'target_id': '123', + 'target_type': 'office', + }, + }, + 'OfficeResource': { + 'list': {}, + 'get': { + 'office_id': '123', + }, + 'assign_number': { + 'office_id': '123', + 'number': '+12223334444', + }, + 'get_operators': { + 'office_id': '123', + }, + 'unassign_number': { + 'office_id': '123', + 'number': '+12223334444', + }, + 'get_call_centers': { + 'office_id': '123', + }, + 'get_departments': { + 'office_id': '123', + }, + 'get_plan': { + 'office_id': '123', + }, + 'update_licenses': { + 'office_id': '123', + 'fax_line_delta': '2', + }, + }, + 'RoomResource': { + 'list': { + 'office_id': '123', + }, + 'create': { + 'name': 'Where it happened', + 'office_id': '123', + }, + 'generate_international_pin': { + 'customer_ref': 'Burr, sir', + }, + 'delete': { + 'room_id': '123', + }, + 'get': { + 'room_id': '123', + }, + 'update': { + 'room_id': '123', + 'name': 'For the last tiiiime', + 'phone_numbers': ['+12223334444'], + }, + 'assign_number': { + 'room_id': '123', + 'number': '+12223334444', + }, + 'unassign_number': { + 'room_id': '123', + 'number': '+12223334444', + }, + 'get_deskphones': { + 'room_id': '123', + }, + 'create_deskphone': { + 'room_id': '123', + 'mac_address': 'Tim Cook', + 'name': 'The red one.', + 'phone_type': 'polycom', + }, + 'delete_deskphone': { + 'room_id': '123', + 'deskphone_id': '123', + }, + 'get_deskphone': { + 'room_id': '123', + 'deskphone_id': '123', + }, + }, + 'SMSResource': { + 'send_sms': { + 'user_id': '123', + 'to_numbers': ['+12223334444'], + 'text': 'Itemized list to follow.', + 'infer_country_code': False, + 'sender_group_id': '123', + 'sender_group_type': 'callcenter', + }, + }, + 'StatsExportResource': { + 'post': { + 'coaching_group': False, + 'days_ago_start': '1', + 'days_ago_end': '2', + 'is_today': False, + 'export_type': 'records', + 'stat_type': 'calls', + 'office_id': '123', + 'target_id': '123', + 'target_type': 'callcenter', + 'timezone': 'America/New_York', + }, + 'get': { + 'export_id': '123', + }, + }, + 'TranscriptResource': { + 'get': { + 'call_id': '123', + }, + }, + 'UserResource': { + 'list': { + 'email': 'tmtesten@test.com', + 'state': 'suspended', + }, + 'create': { + 'email': 'tmtesten@test.com', + 'office_id': '123', + 'first_name': 'Testietta', + 'last_name': 'McTestersen', + 'license': 'lite_support_agents', + }, + 'delete': { + 'user_id': '123', + }, + 'get': { + 'user_id': '123', + }, + 'update': { + 'user_id': '123', + 'admin_office_ids': ['123'], + 'emails': ['tmtesten@test.com'], + 'extension': '123', + 'first_name': 'Testietta', + 'last_name': 'McTestersen', + 'forwarding_numbers': ['+12223334444'], + 'is_super_admin': True, + 'job_title': 'Administraterar', + 'license': 'lite_lines', + 'office_id': '123', + 'phone_numbers': ['+12223334444'], + 'state': 'active', + }, + 'toggle_call_recording': { + 'user_id': '123', + 'is_recording': False, + 'play_message': True, + 'recording_type': 'group', + }, + 'assign_number': { + 'user_id': '123', + 'number': '+12223334444', + }, + 'initiate_call': { + 'user_id': '123', + 'phone_number': '+12223334444', + 'custom_data': 'Y u call self?', + 'group_id': '123', + 'group_type': 'department', + 'outbound_caller_id': 'O.0', + }, + 'unassign_number': { + 'user_id': '123', + 'number': '+12223334444', + }, + 'get_deskphones': { + 'user_id': '123', + }, + 'create_deskphone': { + 'user_id': '123', + 'mac_address': 'Tim Cook', + 'name': 'The red one.', + 'phone_type': 'polycom', + }, + 'delete_deskphone': { + 'user_id': '123', + 'deskphone_id': '123', + }, + 'get_deskphone': { + 'user_id': '123', + 'deskphone_id': '123', + }, + }, + 'UserDeviceResource': { + 'get': { + 'device_id': '123', + }, + 'list': { + 'user_id': '123', + }, + }, + } + + def get_method_example_kwargs(self, resource_instance, resource_method): + """Returns the appropriate kwargs to use when sanity-checking API resource methods.""" + class_msg = 'DialpadResource subclass "%s" must have an entry in EX_METHOD_CALLS' + + class_name = resource_instance.__class__.__name__ + assert class_name in self.EX_METHOD_CALLS, class_msg % class_name + + method_msg = 'Method "%s.%s" must have an entry in EX_METHOD_CALLS' + method_name = resource_method.__name__ + assert method_name in self.EX_METHOD_CALLS[class_name], method_msg % (class_name, method_name) + + return self.EX_METHOD_CALLS[class_name][method_name] + + def _get_resource_submodule_names(self): + """Returns an iterator of python modules that exist in the dialpad/resources directory.""" + for importer, modname, ispkg in pkgutil.iter_modules(resources.__path__): + if modname == 'resource': + continue + + if ispkg: + continue + + yield modname + + def _get_resource_submodules(self): + """Returns an iterator of python modules that are exposed via from dialpad.resources import *""" + for modname in self._get_resource_submodule_names(): + if hasattr(resources, modname): + yield getattr(resources, modname) + + def _get_resource_classes(self): + """Returns an iterator of DialpadResource subclasses that are exposed under dialpad.resources""" + for mod in self._get_resource_submodules(): + for k, v in mod.__dict__.iteritems(): + if not inspect.isclass(v): + continue + + if not issubclass(v, DialpadResource): + continue + + if v == DialpadResource: + continue + + yield v + + def test_resources_properly_imported(self): + """Verifies that all modules definied in the resources directory are properly exposed under + dialpad.resources. + """ + exposed_resources = dir(resources) + + msg = '"%s" module is present in the resources directory, but is not imported in ' \ + 'resources/__init__.py' + + for modname in self._get_resource_submodule_names(): + assert modname in exposed_resources, msg % modname + + def test_resource_classes_properly_exposed(self): + """Verifies that all subclasses of DialpadResource that are defined in the resources directory + are also exposed as direct members of the resources module. + """ + exposed_resources = dir(resources) + + msg = '"%(name)s" resource class is present in the resources package, but is not exposed ' \ + 'directly as resources.%(name)s via resources/__init__.py' + + for c in self._get_resource_classes(): + assert c.__name__ in exposed_resources, msg % {'name': c.__name__} + + def test_request_conformance(self, swagger_stub): + """Verifies that all API requests produced by this library conform to the swagger spec. + + Although this test cannot guarantee that the requests are semantically correct, it can at least + determine whether they are schematically correct. + + This test will also fail if there are no test-kwargs defined in EX_METHOD_CALLS for any public + method implemented by a subclass of DialpadResource. + """ + + # Construct a DialpadClient with a fake API key. + dp = DialpadClient('123') + + # Iterate through the attributes on the client object to find the API resource accessors. + for a in dir(dp): + resource_instance = getattr(dp, a) + + # Skip any attributes that are not DialpadResources + if not isinstance(resource_instance, DialpadResource): + continue + + print '' + print 'Verifying request format of %s methods' % resource_instance.__class__.__name__ + + # Iterate through the attributes on the resource instance. + for method_attr in dir(resource_instance): + # Skip private attributes. + if method_attr.startswith('_'): + continue + + # Skip attributes that are not unique to this particular subclass of DialpadResource. + if hasattr(DialpadResource, method_attr): + continue + + # Skip attributes that are not functions. + resource_method = getattr(resource_instance, method_attr) + if not callable(resource_method): + continue + + # Skip attributes that are not instance methods. + arg_names = inspect.getargspec(resource_method).args + if not arg_names or arg_names[0] != 'self': + continue + + # Fetch example kwargs to test the method (and raise if they haven't been provided). + method_kwargs = self.get_method_example_kwargs(resource_instance, resource_method) + + # Call the method, and allow the swagger mock to raise an exception if it encounters a + # schema error. + print 'Testing %s with kwargs: %s' % (method_attr, method_kwargs) + resource_method(**method_kwargs) diff --git a/test/utils.py b/test/utils.py new file mode 100644 index 0000000..54c8a7e --- /dev/null +++ b/test/utils.py @@ -0,0 +1,54 @@ +import json +import os +import requests + + +RESOURCE_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), '.resources') + + +def resource_filepath(filename): + """Returns a path to the given file name in the test resources directory.""" + return os.path.join(RESOURCE_PATH, filename) + + +def prepare_test_resources(): + """Prepares any resources that are expected to be available at test-time.""" + + # Generate the Dialpad API swagger spec, and write it to a file for easy access. + with open(resource_filepath('swagger_spec.json'), 'w') as f: + json.dump(_generate_swagger_spec(), f) + + +def _generate_swagger_spec(): + """Downloads current Dialpad API swagger spec and returns it as a dict.""" + + # Unfortunately, a little bit of massaging is needed to appease the swagger parser. + def _hotpatch_spec_piece(piece): + if 'type' in piece: + if piece['type'] == 'string' and piece.get('format') == 'int64' and 'default' in piece: + piece['default'] = str(piece['default']) + + if 'operationId' in piece and 'parameters' in piece: + for sub_p in piece['parameters']: + sub_p['required'] = sub_p.get('required', False) + + if 'basePath' in piece: + del piece['basePath'] + + def _hotpatch_spec(spec): + if isinstance(spec, dict): + _hotpatch_spec_piece(spec) + for k, v in spec.iteritems(): + _hotpatch_spec(v) + + elif isinstance(spec, list): + for v in spec: + _hotpatch_spec(v) + + return spec + + # Download the spec from dialpad.com. + spec_json = requests.get('https://dialpad.com/static/openapi/apiv2openapi-en.json').json() + + # Return a patched version that will satisfy the swagger lib. + return _hotpatch_spec(spec_json)