diff --git a/.vscode/settings.json b/.vscode/settings.json index 192df9b..a9a6551 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,7 @@ }, "debug.internalConsoleOptions": "neverOpen", "python.linting.enabled": true, - "python.unitTest.unittestEnabled": false, - "python.unitTest.nosetestsEnabled": false, - "python.unitTest.pyTestEnabled": true + "python.testing.unittestEnabled": false, + "python.testing.nosetestsEnabled": false, + "python.testing.pyTestEnabled": true } diff --git a/Pipfile b/Pipfile index 35423c5..741e294 100644 --- a/Pipfile +++ b/Pipfile @@ -11,3 +11,6 @@ functionapprest = {editable = true,path = "."} [requires] python_version = "3.6" + +[pipenv] +allow_prereleases = true diff --git a/Pipfile.lock b/Pipfile.lock index fce3b73..6d9d7fd 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -16,12 +16,19 @@ ] }, "default": { + "attrs": { + "hashes": [ + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + ], + "version": "==19.1.0" + }, "azure-functions": { "hashes": [ - "sha256:221633dc25e9f0c370f0fce34983c628b0bcb91a782a88644d8702a287b9cf88", - "sha256:548772a3689fd7dc4683a4fd08458ce8e943ff5a1bb08af282caf0845d220d79" + "sha256:23e2c5a6cb0806e6bfb912c54a37cb8b2cec89791e0a2702dd84434530f5101c", + "sha256:c65a74ea3990bfe6d2f74f461624e06e11258ea02659472b61a2e170159afe34" ], - "version": "==1.0.0b3" + "version": "==1.0.0b4" }, "functionapprest": { "editable": true, @@ -29,10 +36,23 @@ }, "jsonschema": { "hashes": [ - "sha256:000e68abd33c972a5248544925a0cae7d1125f9bf6c58280d37546b946769a08", - "sha256:6ff5f3180870836cae40f06fa10419f557208175f13ad7bc26caa77beb1f6e02" + "sha256:0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d", + "sha256:a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a" + ], + "version": "==3.0.1" + }, + "pyrsistent": { + "hashes": [ + "sha256:16692ee739d42cf5e39cef8d27649a8c1fdb7aa99887098f1460057c5eb75c3a" ], - "version": "==2.6.0" + "version": "==0.15.2" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" }, "strict-rfc3339": { "hashes": [ @@ -42,10 +62,10 @@ }, "werkzeug": { "hashes": [ - "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", - "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" + "sha256:865856ebb55c4dcd0630cdd8f3331a1847a819dda7e8c750d3db6f2aa6c0209c", + "sha256:a0b915f0815982fb2a09161cb8f31708052d0951c3ba433ccc5e1aa276507ca6" ], - "version": "==0.14.1" + "version": "==0.15.4" } }, "develop": { @@ -65,17 +85,17 @@ }, "attrs": { "hashes": [ - "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", - "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" ], - "version": "==18.2.0" + "version": "==19.1.0" }, "azure-functions": { "hashes": [ - "sha256:221633dc25e9f0c370f0fce34983c628b0bcb91a782a88644d8702a287b9cf88", - "sha256:548772a3689fd7dc4683a4fd08458ce8e943ff5a1bb08af282caf0845d220d79" + "sha256:23e2c5a6cb0806e6bfb912c54a37cb8b2cec89791e0a2702dd84434530f5101c", + "sha256:c65a74ea3990bfe6d2f74f461624e06e11258ea02659472b61a2e170159afe34" ], - "version": "==1.0.0b3" + "version": "==1.0.0b4" }, "coverage": { "hashes": [ @@ -111,54 +131,49 @@ "editable": true, "path": "." }, + "importlib-metadata": { + "hashes": [ + "sha256:027cfc6524613de726789072f95d2e4cc64dd1dee8096d42d13f2ead5bd302f5", + "sha256:0d05199e1f0b1a8707a1b9c46476d4a49807fb56cb1b0737db1d37feb42fe31d" + ], + "version": "==0.15" + }, "isort": { "hashes": [ - "sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af", - "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8", - "sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497" + "sha256:c40744b6bc5162bbb39c1257fe298b7a393861d50978b565f3ccd9cb9de0182a", + "sha256:f57abacd059dc3bd666258d1efb0377510a89777fda3e3274e3c01f7c03ae22d" ], - "version": "==4.3.4" + "version": "==4.3.20" }, "jsonschema": { "hashes": [ - "sha256:000e68abd33c972a5248544925a0cae7d1125f9bf6c58280d37546b946769a08", - "sha256:6ff5f3180870836cae40f06fa10419f557208175f13ad7bc26caa77beb1f6e02" + "sha256:0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d", + "sha256:a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a" ], - "version": "==2.6.0" + "version": "==3.0.1" }, "lazy-object-proxy": { "hashes": [ - "sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33", - "sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39", - "sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019", - "sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088", - "sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b", - "sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e", - "sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6", - "sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b", - "sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5", - "sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff", - "sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd", - "sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7", - "sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff", - "sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d", - "sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2", - "sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35", - "sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4", - "sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514", - "sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252", - "sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109", - "sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f", - "sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c", - "sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92", - "sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577", - "sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d", - "sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d", - "sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f", - "sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a", - "sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b" - ], - "version": "==1.3.1" + "sha256:159a745e61422217881c4de71f9eafd9d703b93af95618635849fe469a283661", + "sha256:23f63c0821cc96a23332e45dfaa83266feff8adc72b9bcaef86c202af765244f", + "sha256:3b11be575475db2e8a6e11215f5aa95b9ec14de658628776e10d96fa0b4dac13", + "sha256:3f447aff8bc61ca8b42b73304f6a44fa0d915487de144652816f950a3f1ab821", + "sha256:4ba73f6089cd9b9478bc0a4fa807b47dbdb8fad1d8f31a0f0a5dbf26a4527a71", + "sha256:4f53eadd9932055eac465bd3ca1bd610e4d7141e1278012bd1f28646aebc1d0e", + "sha256:64483bd7154580158ea90de5b8e5e6fc29a16a9b4db24f10193f0c1ae3f9d1ea", + "sha256:6f72d42b0d04bfee2397aa1862262654b56922c20a9bb66bb76b6f0e5e4f9229", + "sha256:7c7f1ec07b227bdc561299fa2328e85000f90179a2f44ea30579d38e037cb3d4", + "sha256:7c8b1ba1e15c10b13cad4171cfa77f5bb5ec2580abc5a353907780805ebe158e", + "sha256:8559b94b823f85342e10d3d9ca4ba5478168e1ac5658a8a2f18c991ba9c52c20", + "sha256:a262c7dfb046f00e12a2bdd1bafaed2408114a89ac414b0af8755c696eb3fc16", + "sha256:acce4e3267610c4fdb6632b3886fe3f2f7dd641158a843cf6b6a68e4ce81477b", + "sha256:be089bb6b83fac7f29d357b2dc4cf2b8eb8d98fe9d9ff89f9ea6012970a853c7", + "sha256:bfab710d859c779f273cc48fb86af38d6e9210f38287df0069a63e40b45a2f5c", + "sha256:c10d29019927301d524a22ced72706380de7cfc50f767217485a912b4c8bd82a", + "sha256:dd6e2b598849b3d7aee2295ac765a578879830fb8966f70be8cd472e6069932e", + "sha256:e408f1eacc0a68fed0c08da45f31d0ebb38079f043328dce69ff133b95c29dc1" + ], + "version": "==1.4.1" }, "mccabe": { "hashes": [ @@ -169,25 +184,18 @@ }, "mock": { "hashes": [ - "sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1", - "sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba" + "sha256:83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", + "sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8" ], - "version": "==2.0.0" + "version": "==3.0.5" }, "more-itertools": { "hashes": [ - "sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40", - "sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1" + "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7", + "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a" ], "markers": "python_version > '2.7'", - "version": "==6.0.0" - }, - "pbr": { - "hashes": [ - "sha256:a7953f66e1f82e4b061f43096a4bcc058f7d3d41de9b94ac871770e8bdd831a2", - "sha256:d717573351cfe09f49df61906cd272abaa759b3e91744396b804965ff7bff38b" - ], - "version": "==5.1.2" + "version": "==7.0.0" }, "pep8-naming": { "hashes": [ @@ -198,10 +206,10 @@ }, "pluggy": { "hashes": [ - "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616", - "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a" + "sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", + "sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c" ], - "version": "==0.8.1" + "version": "==0.12.0" }, "prospector": { "hashes": [ @@ -266,9 +274,15 @@ }, "pylint-plugin-utils": { "hashes": [ - "sha256:8ad25a82bcce390d1d6b7c006c123e0cb18051839c9df7b8bdb7823c53fe676e" + "sha256:8d9e31d5ea8b7b0003e1f0f136b44a5235896a32e47c5bc2ef1143e9f6ba0b74" ], - "version": "==0.4" + "version": "==0.5" + }, + "pyrsistent": { + "hashes": [ + "sha256:16692ee739d42cf5e39cef8d27649a8c1fdb7aa99887098f1460057c5eb75c3a" + ], + "version": "==0.15.2" }, "pytest": { "hashes": [ @@ -321,41 +335,48 @@ }, "typed-ast": { "hashes": [ - "sha256:035a54ede6ce1380599b2ce57844c6554666522e376bd111eb940fbc7c3dad23", - "sha256:037c35f2741ce3a9ac0d55abfcd119133cbd821fffa4461397718287092d9d15", - "sha256:049feae7e9f180b64efacbdc36b3af64a00393a47be22fa9cb6794e68d4e73d3", - "sha256:19228f7940beafc1ba21a6e8e070e0b0bfd1457902a3a81709762b8b9039b88d", - "sha256:2ea681e91e3550a30c2265d2916f40a5f5d89b59469a20f3bad7d07adee0f7a6", - "sha256:3a6b0a78af298d82323660df5497bcea0f0a4a25a0b003afd0ce5af049bd1f60", - "sha256:5385da8f3b801014504df0852bf83524599df890387a3c2b17b7caa3d78b1773", - "sha256:606d8afa07eef77280c2bf84335e24390055b478392e1975f96286d99d0cb424", - "sha256:69245b5b23bbf7fb242c9f8f08493e9ecd7711f063259aefffaeb90595d62287", - "sha256:6f6d839ab09830d59b7fa8fb6917023d8cb5498ee1f1dbd82d37db78eb76bc99", - "sha256:730888475f5ac0e37c1de4bd05eeb799fdb742697867f524dc8a4cd74bcecc23", - "sha256:9819b5162ffc121b9e334923c685b0d0826154e41dfe70b2ede2ce29034c71d8", - "sha256:9e60ef9426efab601dd9aa120e4ff560f4461cf8442e9c0a2b92548d52800699", - "sha256:af5fbdde0690c7da68e841d7fc2632345d570768ea7406a9434446d7b33b0ee1", - "sha256:b64efdbdf3bbb1377562c179f167f3bf301251411eb5ac77dec6b7d32bcda463", - "sha256:bac5f444c118aeb456fac1b0b5d14c6a71ea2a42069b09c176f75e9bd4c186f6", - "sha256:bda9068aafb73859491e13b99b682bd299c1b5fd50644d697533775828a28ee0", - "sha256:d659517ca116e6750101a1326107d3479028c5191f0ecee3c7203c50f5b915b0", - "sha256:eddd3fb1f3e0f82e5915a899285a39ee34ce18fd25d89582bc89fc9fb16cd2c6" + "sha256:132eae51d6ef3ff4a8c47c393a4ef5ebf0d1aecc96880eb5d6c8ceab7017cc9b", + "sha256:18141c1484ab8784006c839be8b985cfc82a2e9725837b0ecfa0203f71c4e39d", + "sha256:2baf617f5bbbfe73fd8846463f5aeafc912b5ee247f410700245d68525ec584a", + "sha256:3d90063f2cbbe39177e9b4d888e45777012652d6110156845b828908c51ae462", + "sha256:4304b2218b842d610aa1a1d87e1dc9559597969acc62ce717ee4dfeaa44d7eee", + "sha256:4983ede548ffc3541bae49a82675996497348e55bafd1554dc4e4a5d6eda541a", + "sha256:5315f4509c1476718a4825f45a203b82d7fdf2a6f5f0c8f166435975b1c9f7d4", + "sha256:6cdfb1b49d5345f7c2b90d638822d16ba62dc82f7616e9b4caa10b72f3f16649", + "sha256:7b325f12635598c604690efd7a0197d0b94b7d7778498e76e0710cd582fd1c7a", + "sha256:8d3b0e3b8626615826f9a626548057c5275a9733512b137984a68ba1598d3d2f", + "sha256:8f8631160c79f53081bd23446525db0bc4c5616f78d04021e6e434b286493fd7", + "sha256:912de10965f3dc89da23936f1cc4ed60764f712e5fa603a09dd904f88c996760", + "sha256:b010c07b975fe853c65d7bbe9d4ac62f1c69086750a574f6292597763781ba18", + "sha256:c908c10505904c48081a5415a1e295d8403e353e0c14c42b6d67f8f97fae6616", + "sha256:c94dd3807c0c0610f7c76f078119f4ea48235a953512752b9175f9f98f5ae2bd", + "sha256:ce65dee7594a84c466e79d7fb7d3303e7295d16a83c22c7c4037071b059e2c21", + "sha256:eaa9cfcb221a8a4c2889be6f93da141ac777eb8819f077e1d09fb12d00a09a93", + "sha256:f3376bc31bad66d46d44b4e6522c5c21976bf9bca4ef5987bb2bf727f4506cbb", + "sha256:f9202fa138544e13a4ec1a6792c35834250a85958fde1251b6a22e07d1260ae7" ], "markers": "python_version < '3.7' and implementation_name == 'cpython'", - "version": "==1.3.1" + "version": "==1.3.5" }, "werkzeug": { "hashes": [ - "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", - "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" + "sha256:865856ebb55c4dcd0630cdd8f3331a1847a819dda7e8c750d3db6f2aa6c0209c", + "sha256:a0b915f0815982fb2a09161cb8f31708052d0951c3ba433ccc5e1aa276507ca6" ], - "version": "==0.14.1" + "version": "==0.15.4" }, "wrapt": { "hashes": [ "sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533" ], "version": "==1.11.1" + }, + "zipp": { + "hashes": [ + "sha256:8c1019c6aad13642199fbe458275ad6a84907634cc9f0989877ccc4a2840139d", + "sha256:ca943a7e809cc12257001ccfb99e3563da9af99d52f261725e96dfe0f9275bc3" + ], + "version": "==0.5.1" } } } diff --git a/functionapprest/__init__.py b/functionapprest/__init__.py index 7adffc4..6069330 100755 --- a/functionapprest/__init__.py +++ b/functionapprest/__init__.py @@ -10,13 +10,12 @@ from werkzeug.routing import Map, Rule, NotFound from werkzeug.urls import url_parse from azure.functions import HttpRequest, HttpResponse, Context +from azure.functions._http import HttpResponseHeaders __validate_kwargs = {'format_checker': FormatChecker()} __required_keys = ['method', 'url'] __default_headers = { - 'Access-Control-Allow-Headers': '*', - 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json' } @@ -198,40 +197,6 @@ def set_body(self, body): self.__body_bytes = bytes(body) -class Response(HttpResponse): - """Class to conceptualize a response with default attributes - if no body is specified, empty string is returned - if no status_code is specified, 200 is returned - if no headers are specified, empty dict is returned - """ - - def __init__(self, body=None, status_code=None, headers=None, *, - mimetype='application/json', charset='utf-8'): - self.json = None - if isinstance(body, (dict, list)): - self.json = body - body = json.dumps(body, default=_json_serial) - super(Response, self).__init__(body, status_code=status_code, headers=headers, mimetype=mimetype, charset=charset) - - def get_body_string(self) -> str: - """Response body as a string.""" - - body = self.json - if body is None: - body_bytes = self.get_body() or b'' - body = body_bytes.decode(self.charset) - if body: - return json.dumps(body, default=_json_serial) - return '' - - def to_json(self): - return { - 'body': self.get_body_string(), - 'status_code': self.status_code or 200, - 'headers': self.headers or {} - } - - def _float_cast(value): try: return float(value) @@ -283,11 +248,10 @@ def _options_response(req: Request, methods: list): body = { 'allow': allowed_methods } - headers = __default_headers - headers.update({ + headers = { 'Access-Control-Allow-Methods': allowed_methods - }) - return Response(body, 200, headers) + } + return (body, 200, headers) def default_error_handler(error, method: str): @@ -302,7 +266,7 @@ def default_error_handler(error, method: str): }, 500) -def create_functionapp_handler(error_handler=default_error_handler): +def create_functionapp_handler(error_handler=default_error_handler, headers=None): """Create a functionapp handler function with `handle` decorator as attribute example: @@ -324,6 +288,50 @@ def my_get_func(req): JSON schema, please see http://json-schema.org for info. """ url_maps = Map() + if headers is None: + headers = __default_headers + default_headers = HttpResponseHeaders(headers) + if os.environ.get('AZURE_FUNCTIONS_ENVIRONMENT', '').lower() in ('dev', 'development'): + default_headers.update({ + 'Access-Control-Allow-Headers': '*', + 'Access-Control-Allow-Origin': '*' + }) + + class Response(HttpResponse): + """Class to conceptualize a response with default attributes + if no body is specified, empty string is returned + if no status_code is specified, 200 is returned + if no headers are specified, empty dict is returned + """ + + def __init__(self, body=None, status_code=None, headers=None, *, + mimetype='application/json', charset='utf-8'): + self.json = None + if isinstance(body, (dict, list)): + self.json = body + body = json.dumps(body, default=_json_serial) + original_headers = headers or {} + headers = default_headers + headers.update(original_headers) + super(Response, self).__init__(body, status_code=status_code, headers=headers, mimetype=mimetype, charset=charset) + + def get_body_string(self) -> str: + """Response body as a string.""" + + body = self.json + if body is None: + body_bytes = self.get_body() or b'' + body = body_bytes.decode(self.charset) + if body: + return json.dumps(body, default=_json_serial) + return '' + + def to_json(self): + return { + 'body': self.get_body_string(), + 'status_code': self.status_code or 200, + 'headers': dict(self.headers or {}) + } def inner_functionapp_handler(req: Request, context: FunctionsContext): # check if running as Azure Functions @@ -365,7 +373,8 @@ def inner_functionapp_handler(req: Request, context: FunctionsContext): # bind the mapping to an empty server name mapping = url_maps.bind('') if method_name == 'options': - return _options_response(req, mapping.allowed_methods(path)) + body, status_code, headers = _options_response(req, mapping.allowed_methods(path)) + return Response(body, status_code, headers) rule, kwargs = mapping.match(path, method=method_name, return_rule=True) func = rule.endpoint @@ -425,8 +434,7 @@ def inner_functionapp_handler(req: Request, context: FunctionsContext): def inner_handler(method_name, path='/', schema=None, load_json=True): if schema and not load_json: - raise ValueError( - 'if schema is supplied, load_json needs to be true') + raise ValueError('if schema is supplied, load_json needs to be true') def wrapper(func): @functools.wraps(func) diff --git a/functionapprest/__version__.py b/functionapprest/__version__.py index 8a06e11..b816b54 100755 --- a/functionapprest/__version__.py +++ b/functionapprest/__version__.py @@ -1,3 +1,3 @@ __author__ = 'eduardomourar' __email__ = 'eduardo_demoura@yahoo.com.br' -__version__ = '0.1.2' +__version__ = '0.2.0' diff --git a/setup.py b/setup.py index 5e1bc3d..eb52b08 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ requirements = [ 'jsonschema>=2.5.1', 'strict_rfc3339>=0.7', - 'azure-functions==1.0.0b3', + 'azure-functions>=1.0.0b3', 'werkzeug>=0.14.1', ] diff --git a/tests/test_functionapprest.py b/tests/test_functionapprest.py index 36bee02..0f58ac6 100755 --- a/tests/test_functionapprest.py +++ b/tests/test_functionapprest.py @@ -3,13 +3,15 @@ except ImportError: import mock +import os import unittest import json import copy import random -from datetime import datetime import time +from datetime import datetime + from functionapprest import create_functionapp_handler, Request, FunctionsContext @@ -21,7 +23,7 @@ def assert_called_once(mock): assert mock.call_count == 1 -class TestfunctionapprestFunctions(unittest.TestCase): +class TestFunctions(unittest.TestCase): def setUp(self): self.event = Request('POST', 'http://localhost:7071/api/v1/') self.context = FunctionsContext( @@ -30,7 +32,9 @@ def setUp(self): invocation_id='c9b749e6-0611-4b651-9ff0-cdd2da18f05b', bindings={} ) - self.functionapp_handler = create_functionapp_handler() + self.env = mock.patch.dict('os.environ', {'AZURE_FUNCTIONS_ENVIRONMENT': 'production'}) + with self.env: + self.functionapp_handler = create_functionapp_handler(headers={}) def test_post_validation_success(self): json_body = dict( @@ -343,7 +347,8 @@ def test_exception_in_handler_should_be_reraised(self): def divide_by_zero(_): return 1/0 - self.functionapp_handler = create_functionapp_handler(error_handler=None) + with self.env: + self.functionapp_handler = create_functionapp_handler(error_handler=None, headers={}) self.functionapp_handler.handle('get', path='/foo/bar')(divide_by_zero) with self.assertRaises(ZeroDivisionError): @@ -354,14 +359,64 @@ def test_routing_with_multiple_decorators(self): self.event.set_body(json.dumps(json_body)) self.event.method = 'GET' + headers = { + 'content-type': 'application/json', + 'access-control-allow-origin': '*' + } - self.functionapp_handler = create_functionapp_handler(error_handler=None) + with self.env: + self.functionapp_handler = create_functionapp_handler(error_handler=None, headers={}) def test_routing(event, id): - return {'my-id': id} + return ({'my-id': id}, 200, headers) self.functionapp_handler.handle('get', path='/foo//')(test_routing) self.functionapp_handler.handle('options', path='/foo//')(test_routing) self.event.url = '/foo/1234/' result = self.functionapp_handler(self.event, self.context).to_json() - assert result == {'body': '{"my-id": 1234}', 'status_code': 200, 'headers':{}} + assert result == {'body': '{"my-id": 1234}', 'status_code': 200, 'headers': headers} + + def test_default_header_in_development(self): + json_body = {} + + self.event.set_body(json.dumps(json_body)) + self.event.method = 'GET' + headers = { + 'access-control-allow-headers': '*', + 'content-type': 'application/json', + 'access-control-allow-origin': '*' + } + + with mock.patch.dict('os.environ', {'AZURE_FUNCTIONS_ENVIRONMENT': 'development'}): + environ = os.environ.get('AZURE_FUNCTIONS_ENVIRONMENT', '') + self.functionapp_handler = create_functionapp_handler(error_handler=None, headers=headers) + + def test_env_development(event): + return environ + + self.functionapp_handler.handle('get', path='/foo/')(test_env_development) + self.event.url = '/foo/' + result = self.functionapp_handler(self.event, self.context).to_json() + assert result == {'body': '"development"', 'status_code': 200, 'headers': headers} + + def test_default_header_in_production(self): + json_body = {} + + self.event.set_body(json.dumps(json_body)) + self.event.method = 'GET' + headers = { + 'access-control-allow-headers': '*', + 'content-type': 'application/json' + } + + with mock.patch.dict('os.environ', {'AZURE_FUNCTIONS_ENVIRONMENT': 'production'}): + environ = os.environ.get('AZURE_FUNCTIONS_ENVIRONMENT', '') + self.functionapp_handler = create_functionapp_handler(error_handler=None, headers=headers) + + def test_env_production(event): + return environ + + self.functionapp_handler.handle('get', path='/bar/')(test_env_production) + self.event.url = '/bar/' + result = self.functionapp_handler(self.event, self.context).to_json() + assert result == {'body': '"production"', 'status_code': 200, 'headers': headers}