From 1cc05cd5e6d70a85d2696b7753390fe10442f502 Mon Sep 17 00:00:00 2001 From: David Kilias Date: Wed, 2 Dec 2020 22:47:40 +0100 Subject: [PATCH 01/19] test implementation --- requirements.txt | 2 ++ test_cerberus.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 test_cerberus.py diff --git a/requirements.txt b/requirements.txt index 757931f..bfe4f57 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ tornado==6.0.4 isodate==0.6.0 pytest==5.4.1 +schwifty==2020.11.0 +Cerberus==1.3.2 \ No newline at end of file diff --git a/test_cerberus.py b/test_cerberus.py new file mode 100644 index 0000000..e81e7e6 --- /dev/null +++ b/test_cerberus.py @@ -0,0 +1,76 @@ +#!/usr/bin/python3 + +import cerberus +from schwifty import IBAN, BIC +import datetime + +def valid_iban(field, value, error): + try: + IBAN(value) + return True + except ValueError: + error(field, 'not a valid IBAN') + +def valid_bic(field, value, error): + try: + BIC(value) + return True + except ValueError: + error(field, 'not a valid BIC') + +def iso_date(field, value, error): + try: + datetime.datetime.strptime(value, "%Y-%m-%d") + return True + except ValueError: + error(field, 'not a valid IDO 8601 date') + +document = { + 'finanzdaten': + { + 'bic': 'PBNKDEFFXXX', + 'holder': '', + 'iban': 'DE89370400440532013000', + 'issuance': '2012-01-01', + 'reference': '0042', + 'scan-sepa-mandate': '' + }, + 'mitgliederdaten': + { + 'bis': '', + 'mitgliedsbeitrag': '30', + 'scan-antrag': '', + 'schliessberechtigung': 'Ja', + 'spendenbeitrag': '', + 'status': 'V', + 'von': '2012-01-01' + }, + 'stammdaten': + { + 'address_code': '39104', + 'address_country': 'DE', + 'address_label': 'Max Hackerberg\nLeibnizstr. 32\n39104 Magdeburg', + 'address_locality': 'Magdeburg', + 'address_region': '', + 'address_street': 'Leibnizstr. 32', + 'birth_date': '1970-01-01', + 'birth_location': 'Hackstadt', + 'email': 'max.hackerberg@netz39.de', + 'fullname': 'Max Hackerberg', + 'nickname': 'maxH', + 'pgp-key': '', + 'ssh-key': '' + }, + 'timestamp': '2020-03-25T23:58:11', + 'id': '6af68' + } + +schema_fin={'bic': {'type': 'string', 'check_with': valid_bic}, + 'iban': {'type': 'string', 'check_with': valid_iban}, + 'issuance': {'type': 'string', 'check_with': iso_date}, + 'reference': {'type': 'string'}, + 'scan-sepa-mandate': {'type': 'string'}, + 'holder': {'type': 'string'}} + +v = cerberus.Validator() +v.validate(document['finanzdaten'], schema_fin) \ No newline at end of file -- 2.45.2 From 0376228d305678e0d63328780149981a11d5f0eb Mon Sep 17 00:00:00 2001 From: David Kilias Date: Wed, 2 Dec 2020 23:30:31 +0100 Subject: [PATCH 02/19] mehr und komplexe schemata --- test_cerberus.py | 55 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/test_cerberus.py b/test_cerberus.py index e81e7e6..6921f7f 100644 --- a/test_cerberus.py +++ b/test_cerberus.py @@ -23,7 +23,14 @@ def iso_date(field, value, error): datetime.datetime.strptime(value, "%Y-%m-%d") return True except ValueError: - error(field, 'not a valid IDO 8601 date') + error(field, 'not a valid ISO 8601 date') + +def valid_money_amount(field, value, error): + try: + float(value) + return True + except (ValueError, TypeError): + error(field, 'not a valid money value') document = { 'finanzdaten': @@ -41,7 +48,7 @@ document = { 'mitgliedsbeitrag': '30', 'scan-antrag': '', 'schliessberechtigung': 'Ja', - 'spendenbeitrag': '', + 'spendenbeitrag': '0', 'status': 'V', 'von': '2012-01-01' }, @@ -64,13 +71,47 @@ document = { 'timestamp': '2020-03-25T23:58:11', 'id': '6af68' } - -schema_fin={'bic': {'type': 'string', 'check_with': valid_bic}, - 'iban': {'type': 'string', 'check_with': valid_iban}, - 'issuance': {'type': 'string', 'check_with': iso_date}, + +schema_fin={ + 'bic': { + 'type': 'string', + 'required': True, + 'check_with': valid_bic}, + 'iban': { + 'type': 'string', + 'required': True, + 'check_with': valid_iban}, + 'issuance': { + 'type': 'string', + 'required': True, + 'check_with': iso_date}, 'reference': {'type': 'string'}, 'scan-sepa-mandate': {'type': 'string'}, 'holder': {'type': 'string'}} +schema_member={'bis': { + 'type': 'string', + 'oneof': [{'check_with': iso_date},{'empty': True}]}, + 'mitgliedsbeitrag': { + 'type': 'string', + 'check_with': valid_money_amount}, + 'scan-antrag': {'type': 'string'}, + 'schliessberechtigung': { + 'type': 'string', + 'allowed': ['Ja', 'Nein', 'J', 'N', 'j', 'n', 'y', 'Y']}, + 'spendenbeitrag': { + 'type': 'string', + 'check_with': valid_money_amount}, + 'status': { + 'type': 'string', + 'allowed': ['V', 'E', 'F']}, + 'von': { + 'type': 'string', + 'required': True, + 'check_with': iso_date}} + v = cerberus.Validator() -v.validate(document['finanzdaten'], schema_fin) \ No newline at end of file +print(v.validate(document['finanzdaten'], schema_fin)) +print(v.errors) +print(v.validate(document['mitgliederdaten'], schema_member)) +print(v.errors) \ No newline at end of file -- 2.45.2 From 7671739300eb2ca0e652fcb6d56262b0465c97e3 Mon Sep 17 00:00:00 2001 From: David Kilias Date: Fri, 4 Dec 2020 20:57:30 +0100 Subject: [PATCH 03/19] =?UTF-8?q?test=20f=C3=BCr=20ein=20komplettes=20enti?= =?UTF-8?q?ties=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test_cerberus.py | 129 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 41 deletions(-) diff --git a/test_cerberus.py b/test_cerberus.py index 6921f7f..87f388e 100644 --- a/test_cerberus.py +++ b/test_cerberus.py @@ -1,6 +1,6 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 -import cerberus +from cerberus import Validator from schwifty import IBAN, BIC import datetime @@ -73,45 +73,92 @@ document = { } schema_fin={ - 'bic': { - 'type': 'string', - 'required': True, - 'check_with': valid_bic}, - 'iban': { - 'type': 'string', - 'required': True, - 'check_with': valid_iban}, - 'issuance': { - 'type': 'string', - 'required': True, - 'check_with': iso_date}, - 'reference': {'type': 'string'}, - 'scan-sepa-mandate': {'type': 'string'}, - 'holder': {'type': 'string'}} + 'bic': { + 'type': 'string', + 'required': True, + 'check_with': valid_bic}, + 'iban': { + 'type': 'string', + 'required': True, + 'check_with': valid_iban}, + 'issuance': { + 'type': 'string', + 'required': True, + 'check_with': iso_date}, + 'reference': {'type': 'string'}, + 'scan-sepa-mandate': {'type': 'string'}, + 'holder': {'type': 'string'}} -schema_member={'bis': { - 'type': 'string', - 'oneof': [{'check_with': iso_date},{'empty': True}]}, - 'mitgliedsbeitrag': { - 'type': 'string', - 'check_with': valid_money_amount}, - 'scan-antrag': {'type': 'string'}, - 'schliessberechtigung': { - 'type': 'string', - 'allowed': ['Ja', 'Nein', 'J', 'N', 'j', 'n', 'y', 'Y']}, - 'spendenbeitrag': { - 'type': 'string', - 'check_with': valid_money_amount}, - 'status': { - 'type': 'string', - 'allowed': ['V', 'E', 'F']}, - 'von': { - 'type': 'string', - 'required': True, - 'check_with': iso_date}} +schema_membership={ + 'bis': { + 'type': 'string', + 'oneof': [{'check_with': iso_date},{'empty': True}]}, + 'mitgliedsbeitrag': { + 'type': 'string', + 'check_with': valid_money_amount}, + 'scan-antrag': {'type': 'string'}, + 'schliessberechtigung': { + 'type': 'string', + 'allowed': ['Ja', 'Nein', 'J', 'N', 'j', 'n', 'y', 'Y']}, + 'spendenbeitrag': { + 'type': 'string', + 'check_with': valid_money_amount}, + 'status': { + 'type': 'string', + 'allowed': ['V', 'E', 'F']}, + 'von': { + 'type': 'string', + 'required': True, + 'check_with': iso_date}} -v = cerberus.Validator() -print(v.validate(document['finanzdaten'], schema_fin)) -print(v.errors) -print(v.validate(document['mitgliederdaten'], schema_member)) +schema_base={ + 'address_code': { + 'type': 'string'}, + 'address_country': { + 'type': 'string'}, + 'address_label': { + 'type': 'string'}, + 'address_locality': { + 'type': 'string'}, + 'address_region': { + 'type': 'string'}, + 'address_street': { + 'type': 'string'}, + 'birth_date': { + 'type': 'string', + 'required': True, + 'check_with': iso_date}, + 'birth_location': { + 'type': 'string'}, + 'email': { + 'type': 'string', + 'regex': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'}, + 'fullname': { + 'type': 'string'}, + 'nickname': { + 'type': 'string'}, + 'pgp-key': { + 'type': 'string'}, + 'ssh-key': { + 'type': 'string'}} + +schema = { + 'finanzdaten': { + 'type': 'dict', + 'schema': schema_fin}, + 'mitgliederdaten': { + 'type': 'dict', + 'schema': schema_membership}, + 'stammdaten': { + 'type': 'dict', + 'schema': schema_base}, + 'id': { + 'type': 'string', + 'regex': '^[a-f0-9]{5}$'}, + 'timestamp': { + 'type': 'string'} +} + +v = Validator() +print(v.validate(document, schema)) print(v.errors) \ No newline at end of file -- 2.45.2 From e3557f5d52085c404dbf84839389a6832a8aae2e Mon Sep 17 00:00:00 2001 From: David Kilias Date: Fri, 4 Dec 2020 21:02:55 +0100 Subject: [PATCH 04/19] refactor validation functions --- test_cerberus.py | 31 +------------------------------ validators.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 30 deletions(-) create mode 100644 validators.py diff --git a/test_cerberus.py b/test_cerberus.py index 87f388e..fa4ace3 100644 --- a/test_cerberus.py +++ b/test_cerberus.py @@ -1,36 +1,7 @@ #!/usr/bin/env python3 from cerberus import Validator -from schwifty import IBAN, BIC -import datetime - -def valid_iban(field, value, error): - try: - IBAN(value) - return True - except ValueError: - error(field, 'not a valid IBAN') - -def valid_bic(field, value, error): - try: - BIC(value) - return True - except ValueError: - error(field, 'not a valid BIC') - -def iso_date(field, value, error): - try: - datetime.datetime.strptime(value, "%Y-%m-%d") - return True - except ValueError: - error(field, 'not a valid ISO 8601 date') - -def valid_money_amount(field, value, error): - try: - float(value) - return True - except (ValueError, TypeError): - error(field, 'not a valid money value') +from validators import * document = { 'finanzdaten': diff --git a/validators.py b/validators.py new file mode 100644 index 0000000..c4c270c --- /dev/null +++ b/validators.py @@ -0,0 +1,30 @@ +from schwifty import IBAN, BIC +import datetime + +def valid_iban(field, value, error): + try: + IBAN(value) + return True + except ValueError: + error(field, 'not a valid IBAN') + +def valid_bic(field, value, error): + try: + BIC(value) + return True + except ValueError: + error(field, 'not a valid BIC') + +def iso_date(field, value, error): + try: + datetime.datetime.strptime(value, "%Y-%m-%d") + return True + except ValueError: + error(field, 'not a valid ISO 8601 date') + +def valid_money_amount(field, value, error): + try: + float(value) + return True + except (ValueError, TypeError): + error(field, 'not a valid money value') \ No newline at end of file -- 2.45.2 From 5154646ca2c4e4755cc4e54714ac46693cb15e2c Mon Sep 17 00:00:00 2001 From: David Kilias Date: Fri, 4 Dec 2020 21:03:09 +0100 Subject: [PATCH 05/19] more constraints on fields --- test_cerberus.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test_cerberus.py b/test_cerberus.py index fa4ace3..dca081a 100644 --- a/test_cerberus.py +++ b/test_cerberus.py @@ -76,6 +76,7 @@ schema_membership={ 'check_with': valid_money_amount}, 'status': { 'type': 'string', + 'required': True, 'allowed': ['V', 'E', 'F']}, 'von': { 'type': 'string', @@ -103,9 +104,11 @@ schema_base={ 'type': 'string'}, 'email': { 'type': 'string', + 'required': True, 'regex': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'}, 'fullname': { - 'type': 'string'}, + 'type': 'string', + 'required': True}, 'nickname': { 'type': 'string'}, 'pgp-key': { @@ -125,9 +128,11 @@ schema = { 'schema': schema_base}, 'id': { 'type': 'string', + 'required': True, 'regex': '^[a-f0-9]{5}$'}, 'timestamp': { - 'type': 'string'} + 'type': 'string', + 'required': True} } v = Validator() -- 2.45.2 From 403ce3a258abe82af7b361d30bb2335abc95b6d1 Mon Sep 17 00:00:00 2001 From: David Kilias Date: Fri, 4 Dec 2020 21:18:19 +0100 Subject: [PATCH 06/19] some renaming --- entity_validator.py | 103 +++++++++++++++++ test_cerberus.py | 140 ----------------------- validators.py => validation_functions.py | 0 3 files changed, 103 insertions(+), 140 deletions(-) create mode 100644 entity_validator.py delete mode 100644 test_cerberus.py rename validators.py => validation_functions.py (100%) diff --git a/entity_validator.py b/entity_validator.py new file mode 100644 index 0000000..3c5b66d --- /dev/null +++ b/entity_validator.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +from cerberus import Validator +from validation_functions import * + +def validate(entity): + schema_fin={ + 'bic': { + 'type': 'string', + 'required': True, + 'check_with': valid_bic}, + 'iban': { + 'type': 'string', + 'required': True, + 'check_with': valid_iban}, + 'issuance': { + 'type': 'string', + 'required': True, + 'check_with': iso_date}, + 'reference': {'type': 'string'}, + 'scan-sepa-mandate': {'type': 'string'}, + 'holder': {'type': 'string'}} + + schema_membership={ + 'bis': { + 'type': 'string', + 'oneof': [{'check_with': iso_date},{'empty': True}]}, + 'mitgliedsbeitrag': { + 'type': 'string', + 'check_with': valid_money_amount}, + 'scan-antrag': {'type': 'string'}, + 'schliessberechtigung': { + 'type': 'string', + 'allowed': ['Ja', 'Nein', 'J', 'N', 'j', 'n', 'y', 'Y']}, + 'spendenbeitrag': { + 'type': 'string', + 'check_with': valid_money_amount}, + 'status': { + 'type': 'string', + 'required': True, + 'allowed': ['V', 'E', 'F']}, + 'von': { + 'type': 'string', + 'required': True, + 'check_with': iso_date}} + + schema_base={ + 'address_code': { + 'type': 'string'}, + 'address_country': { + 'type': 'string'}, + 'address_label': { + 'type': 'string'}, + 'address_locality': { + 'type': 'string'}, + 'address_region': { + 'type': 'string'}, + 'address_street': { + 'type': 'string'}, + 'birth_date': { + 'type': 'string', + 'required': True, + 'check_with': iso_date}, + 'birth_location': { + 'type': 'string'}, + 'email': { + 'type': 'string', + 'required': True, + 'regex': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$'}, + 'fullname': { + 'type': 'string', + 'required': True}, + 'nickname': { + 'type': 'string'}, + 'pgp-key': { + 'type': 'string'}, + 'ssh-key': { + 'type': 'string'}} + + schema = { + 'finanzdaten': { + 'type': 'dict', + 'schema': schema_fin}, + 'mitgliederdaten': { + 'type': 'dict', + 'schema': schema_membership}, + 'stammdaten': { + 'type': 'dict', + 'schema': schema_base}, + 'id': { + 'type': 'string', + 'required': True, + 'regex': '^[a-f0-9]{5}$'}, + 'timestamp': { + 'type': 'string', + 'required': True} + } + + v = Validator() + result = { + 'valid': v.validate(entity, schema), + 'errors': v.errors} + return result \ No newline at end of file diff --git a/test_cerberus.py b/test_cerberus.py deleted file mode 100644 index dca081a..0000000 --- a/test_cerberus.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python3 - -from cerberus import Validator -from validators import * - -document = { - 'finanzdaten': - { - 'bic': 'PBNKDEFFXXX', - 'holder': '', - 'iban': 'DE89370400440532013000', - 'issuance': '2012-01-01', - 'reference': '0042', - 'scan-sepa-mandate': '' - }, - 'mitgliederdaten': - { - 'bis': '', - 'mitgliedsbeitrag': '30', - 'scan-antrag': '', - 'schliessberechtigung': 'Ja', - 'spendenbeitrag': '0', - 'status': 'V', - 'von': '2012-01-01' - }, - 'stammdaten': - { - 'address_code': '39104', - 'address_country': 'DE', - 'address_label': 'Max Hackerberg\nLeibnizstr. 32\n39104 Magdeburg', - 'address_locality': 'Magdeburg', - 'address_region': '', - 'address_street': 'Leibnizstr. 32', - 'birth_date': '1970-01-01', - 'birth_location': 'Hackstadt', - 'email': 'max.hackerberg@netz39.de', - 'fullname': 'Max Hackerberg', - 'nickname': 'maxH', - 'pgp-key': '', - 'ssh-key': '' - }, - 'timestamp': '2020-03-25T23:58:11', - 'id': '6af68' - } - -schema_fin={ - 'bic': { - 'type': 'string', - 'required': True, - 'check_with': valid_bic}, - 'iban': { - 'type': 'string', - 'required': True, - 'check_with': valid_iban}, - 'issuance': { - 'type': 'string', - 'required': True, - 'check_with': iso_date}, - 'reference': {'type': 'string'}, - 'scan-sepa-mandate': {'type': 'string'}, - 'holder': {'type': 'string'}} - -schema_membership={ - 'bis': { - 'type': 'string', - 'oneof': [{'check_with': iso_date},{'empty': True}]}, - 'mitgliedsbeitrag': { - 'type': 'string', - 'check_with': valid_money_amount}, - 'scan-antrag': {'type': 'string'}, - 'schliessberechtigung': { - 'type': 'string', - 'allowed': ['Ja', 'Nein', 'J', 'N', 'j', 'n', 'y', 'Y']}, - 'spendenbeitrag': { - 'type': 'string', - 'check_with': valid_money_amount}, - 'status': { - 'type': 'string', - 'required': True, - 'allowed': ['V', 'E', 'F']}, - 'von': { - 'type': 'string', - 'required': True, - 'check_with': iso_date}} - -schema_base={ - 'address_code': { - 'type': 'string'}, - 'address_country': { - 'type': 'string'}, - 'address_label': { - 'type': 'string'}, - 'address_locality': { - 'type': 'string'}, - 'address_region': { - 'type': 'string'}, - 'address_street': { - 'type': 'string'}, - 'birth_date': { - 'type': 'string', - 'required': True, - 'check_with': iso_date}, - 'birth_location': { - 'type': 'string'}, - 'email': { - 'type': 'string', - 'required': True, - 'regex': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'}, - 'fullname': { - 'type': 'string', - 'required': True}, - 'nickname': { - 'type': 'string'}, - 'pgp-key': { - 'type': 'string'}, - 'ssh-key': { - 'type': 'string'}} - -schema = { - 'finanzdaten': { - 'type': 'dict', - 'schema': schema_fin}, - 'mitgliederdaten': { - 'type': 'dict', - 'schema': schema_membership}, - 'stammdaten': { - 'type': 'dict', - 'schema': schema_base}, - 'id': { - 'type': 'string', - 'required': True, - 'regex': '^[a-f0-9]{5}$'}, - 'timestamp': { - 'type': 'string', - 'required': True} -} - -v = Validator() -print(v.validate(document, schema)) -print(v.errors) \ No newline at end of file diff --git a/validators.py b/validation_functions.py similarity index 100% rename from validators.py rename to validation_functions.py -- 2.45.2 From b32dfda01eed942ce25752e9c77e81f54542a089 Mon Sep 17 00:00:00 2001 From: David Kilias Date: Fri, 4 Dec 2020 21:18:37 +0100 Subject: [PATCH 07/19] use entity validator in app --- app.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index 96a27ef..26f16af 100644 --- a/app.py +++ b/app.py @@ -12,6 +12,7 @@ import json import util +from entity_validator import validate startup_timestamp = datetime.now() @@ -74,12 +75,9 @@ class ValidateHandler(tornado.web.RequestHandler, metaclass=ABCMeta): entity = self.request.body.decode() - # TODO call validator - validation_result = { - "valid": "true" - } + validation_result = validate(json.loads(entity)) - self.write(validation_result) + self.write(json.dumps(validation_result)) self.finish() -- 2.45.2 From 79d60dc4d4376c6c4e191a63afc67c2cee29084a Mon Sep 17 00:00:00 2001 From: David Kilias Date: Fri, 4 Dec 2020 21:18:56 +0100 Subject: [PATCH 08/19] add testcase for actual entity --- test.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test.py b/test.py index ee268d3..06c0165 100644 --- a/test.py +++ b/test.py @@ -54,6 +54,58 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): validation_result = json.loads(response.body.decode()) + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + + def test_valid_entity(self): + entity = { + 'finanzdaten': + { + 'bic': 'PBNKDEFFXXX', + 'holder': '', + 'iban': 'DE89370400440532013000', + 'issuance': '2012-01-01', + 'reference': '0042', + 'scan-sepa-mandate': '' + }, + 'mitgliederdaten': + { + 'bis': '', + 'mitgliedsbeitrag': '30', + 'scan-antrag': '', + 'schliessberechtigung': 'Ja', + 'spendenbeitrag': '0', + 'status': 'V', + 'von': '2012-01-01' + }, + 'stammdaten': + { + 'address_code': '39104', + 'address_country': 'DE', + 'address_label': 'Max Hackerberg\nLeibnizstr. 32\n39104 Magdeburg', + 'address_locality': 'Magdeburg', + 'address_region': '', + 'address_street': 'Leibnizstr. 32', + 'birth_date': '1970-01-01', + 'birth_location': 'Hackstadt', + 'email': 'max.hackerberg@netz39.de', + 'fullname': 'Max Hackerberg', + 'nickname': 'maxH', + 'pgp-key': '', + 'ssh-key': '' + }, + 'timestamp': '2020-03-25T23:58:11', + 'id': '6af68' + } + + response = self.fetch('/v0/validate', + method='POST', + body=json.dumps(entity)) + + self.assertEqual(200, response.code, "Validation must always return 200") + + validation_result = json.loads(response.body.decode()) + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") self.assertTrue(validation_result['valid'], "Validation result is expected to be valid==true") -- 2.45.2 From 25a0f902ab174320d384b9423f183572d2123dea Mon Sep 17 00:00:00 2001 From: David Kilias Date: Fri, 4 Dec 2020 21:23:07 +0100 Subject: [PATCH 09/19] sub dicts are required --- entity_validator.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/entity_validator.py b/entity_validator.py index 3c5b66d..fbcd49e 100644 --- a/entity_validator.py +++ b/entity_validator.py @@ -79,13 +79,16 @@ def validate(entity): schema = { 'finanzdaten': { - 'type': 'dict', + 'type': 'dict', + 'required': True, 'schema': schema_fin}, 'mitgliederdaten': { - 'type': 'dict', + 'type': 'dict', + 'required': True, 'schema': schema_membership}, 'stammdaten': { - 'type': 'dict', + 'type': 'dict', + 'required': True, 'schema': schema_base}, 'id': { 'type': 'string', -- 2.45.2 From 6758bb5a442f5a3ca2db2cbfc9259351e3047866 Mon Sep 17 00:00:00 2001 From: David Kilias Date: Sun, 6 Dec 2020 21:35:39 +0100 Subject: [PATCH 10/19] test case in datei ausgelagert --- test.py | 42 ++----------------------------------- test_cases/valid/valid.json | 36 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 40 deletions(-) create mode 100644 test_cases/valid/valid.json diff --git a/test.py b/test.py index 06c0165..43dd022 100644 --- a/test.py +++ b/test.py @@ -58,49 +58,11 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") def test_valid_entity(self): - entity = { - 'finanzdaten': - { - 'bic': 'PBNKDEFFXXX', - 'holder': '', - 'iban': 'DE89370400440532013000', - 'issuance': '2012-01-01', - 'reference': '0042', - 'scan-sepa-mandate': '' - }, - 'mitgliederdaten': - { - 'bis': '', - 'mitgliedsbeitrag': '30', - 'scan-antrag': '', - 'schliessberechtigung': 'Ja', - 'spendenbeitrag': '0', - 'status': 'V', - 'von': '2012-01-01' - }, - 'stammdaten': - { - 'address_code': '39104', - 'address_country': 'DE', - 'address_label': 'Max Hackerberg\nLeibnizstr. 32\n39104 Magdeburg', - 'address_locality': 'Magdeburg', - 'address_region': '', - 'address_street': 'Leibnizstr. 32', - 'birth_date': '1970-01-01', - 'birth_location': 'Hackstadt', - 'email': 'max.hackerberg@netz39.de', - 'fullname': 'Max Hackerberg', - 'nickname': 'maxH', - 'pgp-key': '', - 'ssh-key': '' - }, - 'timestamp': '2020-03-25T23:58:11', - 'id': '6af68' - } + entity_file = json.load(open('test_cases/valid/valid.json','r')) response = self.fetch('/v0/validate', method='POST', - body=json.dumps(entity)) + body=json.dumps(entity_file)) self.assertEqual(200, response.code, "Validation must always return 200") diff --git a/test_cases/valid/valid.json b/test_cases/valid/valid.json new file mode 100644 index 0000000..d11361e --- /dev/null +++ b/test_cases/valid/valid.json @@ -0,0 +1,36 @@ +{ + "finanzdaten": { + "bic": "PBNKDEFFXXX", + "holder": "", + "iban": "DE89370400440532013000", + "issuance": "2012-01-01", + "reference": "0042", + "scan-sepa-mandate": "" + }, + "mitgliederdaten": { + "bis": "", + "mitgliedsbeitrag": "30", + "scan-antrag": "", + "schliessberechtigung": "Ja", + "spendenbeitrag": "0", + "status": "V", + "von": "2012-01-01" + }, + "stammdaten": { + "address_code": "39104", + "address_country": "DE", + "address_label": "Max Hackerberg\nLeibnizstr. 32\n39104 Magdeburg", + "address_locality": "Magdeburg", + "address_region": "", + "address_street": "Leibnizstr. 32", + "birth_date": "1970-01-01", + "birth_location": "Hackstadt", + "email": "max.hackerberg@netz39.de", + "fullname": "Max Hackerberg", + "nickname": "maxH", + "pgp-key": "", + "ssh-key": "" + }, + "timestamp": "2020-03-25T23:58:11", + "id": "6af68" +} \ No newline at end of file -- 2.45.2 From fee14d93491d291a0ea2faf66c9f8e1d408d7bdb Mon Sep 17 00:00:00 2001 From: David Kilias Date: Sun, 6 Dec 2020 21:37:18 +0100 Subject: [PATCH 11/19] =?UTF-8?q?money=20amount=20validation=20funktion=20?= =?UTF-8?q?mit=20erkl=C3=A4rendem=20kommentar=20versehen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- validation_functions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/validation_functions.py b/validation_functions.py index c4c270c..45c0291 100644 --- a/validation_functions.py +++ b/validation_functions.py @@ -24,6 +24,7 @@ def iso_date(field, value, error): def valid_money_amount(field, value, error): try: + # value is string, check formatting by parsing as float float(value) return True except (ValueError, TypeError): -- 2.45.2 From 0082ee2bad9bf66cac204667d91b2d27e6604ddd Mon Sep 17 00:00:00 2001 From: David Kilias Date: Sun, 6 Dec 2020 21:54:36 +0100 Subject: [PATCH 12/19] =?UTF-8?q?validator=20bibliothek=20statt=20eigenen?= =?UTF-8?q?=20regex=20f=C3=BCr=20email=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- entity_validator.py | 2 +- requirements.txt | 3 ++- validation_functions.py | 10 +++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/entity_validator.py b/entity_validator.py index fbcd49e..6bbb497 100644 --- a/entity_validator.py +++ b/entity_validator.py @@ -66,7 +66,7 @@ def validate(entity): 'email': { 'type': 'string', 'required': True, - 'regex': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$'}, + 'check_with': valid_email}, 'fullname': { 'type': 'string', 'required': True}, diff --git a/requirements.txt b/requirements.txt index bfe4f57..04e43f1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ tornado==6.0.4 isodate==0.6.0 pytest==5.4.1 schwifty==2020.11.0 -Cerberus==1.3.2 \ No newline at end of file +Cerberus==1.3.2 +validator-collection==1.5.0 \ No newline at end of file diff --git a/validation_functions.py b/validation_functions.py index 45c0291..9e5b29a 100644 --- a/validation_functions.py +++ b/validation_functions.py @@ -1,5 +1,6 @@ from schwifty import IBAN, BIC import datetime +from validator_collection import validators, errors def valid_iban(field, value, error): try: @@ -28,4 +29,11 @@ def valid_money_amount(field, value, error): float(value) return True except (ValueError, TypeError): - error(field, 'not a valid money value') \ No newline at end of file + error(field, 'not a valid money value') + +def valid_email(field, value, error): + try: + validators.email(value) + return True + except errors.InvalidEmailError: + error(field, 'not a valid email') \ No newline at end of file -- 2.45.2 From d22d3b61e88bf44552658ec99df39242f1e4d0dd Mon Sep 17 00:00:00 2001 From: David Kilias Date: Sun, 6 Dec 2020 22:06:53 +0100 Subject: [PATCH 13/19] invalid iban testcase --- test.py | 14 +++++++++++ test_cases/invalid/invalid_iban.json | 36 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 test_cases/invalid/invalid_iban.json diff --git a/test.py b/test.py index 43dd022..c562bdc 100644 --- a/test.py +++ b/test.py @@ -71,6 +71,20 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") self.assertTrue(validation_result['valid'], "Validation result is expected to be valid==true") + def test_invalid_entity(self): + entity_file = json.load(open('test_cases/invalid/invalid_iban.json','r')) + + response = self.fetch('/v0/validate', + method='POST', + body=json.dumps(entity_file)) + + self.assertEqual(200, response.code, "Validation must always return 200") + + validation_result = json.loads(response.body.decode()) + + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + if __name__ == "__main__": unittest.main() diff --git a/test_cases/invalid/invalid_iban.json b/test_cases/invalid/invalid_iban.json new file mode 100644 index 0000000..c1357ee --- /dev/null +++ b/test_cases/invalid/invalid_iban.json @@ -0,0 +1,36 @@ +{ + "finanzdaten": { + "bic": "PBNKDEFFXXX", + "holder": "", + "iban": "DX89370400440532013000", + "issuance": "2012-01-01", + "reference": "0042", + "scan-sepa-mandate": "" + }, + "mitgliederdaten": { + "bis": "", + "mitgliedsbeitrag": "30", + "scan-antrag": "", + "schliessberechtigung": "Ja", + "spendenbeitrag": "0", + "status": "V", + "von": "2012-01-01" + }, + "stammdaten": { + "address_code": "39104", + "address_country": "DE", + "address_label": "Max Hackerberg\nLeibnizstr. 32\n39104 Magdeburg", + "address_locality": "Magdeburg", + "address_region": "", + "address_street": "Leibnizstr. 32", + "birth_date": "1970-01-01", + "birth_location": "Hackstadt", + "email": "max.hackerberg@netz39.de", + "fullname": "Max Hackerberg", + "nickname": "maxH", + "pgp-key": "", + "ssh-key": "" + }, + "timestamp": "2020-03-25T23:58:11", + "id": "6af68" +} \ No newline at end of file -- 2.45.2 From eaf6aeeccc7a590c28127a5b0a0d92a33f1fd09e Mon Sep 17 00:00:00 2001 From: David Kilias Date: Sun, 6 Dec 2020 22:10:52 +0100 Subject: [PATCH 14/19] id field testcases --- test.py | 31 ++++++++++++++++++++++++- test_cases/invalid/invalid_id.json | 36 ++++++++++++++++++++++++++++++ test_cases/invalid/missing_id.json | 36 ++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test_cases/invalid/invalid_id.json create mode 100644 test_cases/invalid/missing_id.json diff --git a/test.py b/test.py index c562bdc..0aa7420 100644 --- a/test.py +++ b/test.py @@ -71,7 +71,7 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") self.assertTrue(validation_result['valid'], "Validation result is expected to be valid==true") - def test_invalid_entity(self): + def test_invalid_iban(self): entity_file = json.load(open('test_cases/invalid/invalid_iban.json','r')) response = self.fetch('/v0/validate', @@ -85,6 +85,35 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + def test_missing_id(self): + entity_file = json.load(open('test_cases/invalid/missing_id.json','r')) + + response = self.fetch('/v0/validate', + method='POST', + body=json.dumps(entity_file)) + + self.assertEqual(200, response.code, "Validation must always return 200") + + validation_result = json.loads(response.body.decode()) + + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + self.assertIn('id', validation_result['errors']) + + def test_invalid_id(self): + entity_file = json.load(open('test_cases/invalid/invalid_id.json','r')) + + response = self.fetch('/v0/validate', + method='POST', + body=json.dumps(entity_file)) + + self.assertEqual(200, response.code, "Validation must always return 200") + + validation_result = json.loads(response.body.decode()) + + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + self.assertIn('id', validation_result['errors']) if __name__ == "__main__": unittest.main() diff --git a/test_cases/invalid/invalid_id.json b/test_cases/invalid/invalid_id.json new file mode 100644 index 0000000..ed6e759 --- /dev/null +++ b/test_cases/invalid/invalid_id.json @@ -0,0 +1,36 @@ +{ + "finanzdaten": { + "bic": "PBNKDEFFXXX", + "holder": "", + "iban": "DE89370400440532013000", + "issuance": "2012-01-01", + "reference": "0042", + "scan-sepa-mandate": "" + }, + "mitgliederdaten": { + "bis": "", + "mitgliedsbeitrag": "30", + "scan-antrag": "", + "schliessberechtigung": "Ja", + "spendenbeitrag": "0", + "status": "V", + "von": "2012-01-01" + }, + "stammdaten": { + "address_code": "39104", + "address_country": "DE", + "address_label": "Max Hackerberg\nLeibnizstr. 32\n39104 Magdeburg", + "address_locality": "Magdeburg", + "address_region": "", + "address_street": "Leibnizstr. 32", + "birth_date": "1970-01-01", + "birth_location": "Hackstadt", + "email": "max.hackerberg@netz39.de", + "fullname": "Max Hackerberg", + "nickname": "maxH", + "pgp-key": "", + "ssh-key": "" + }, + "timestamp": "2020-03-25T23:58:11", + "id": "6ay68" +} \ No newline at end of file diff --git a/test_cases/invalid/missing_id.json b/test_cases/invalid/missing_id.json new file mode 100644 index 0000000..21beb16 --- /dev/null +++ b/test_cases/invalid/missing_id.json @@ -0,0 +1,36 @@ +{ + "finanzdaten": { + "bic": "PBNKDEFFXXX", + "holder": "", + "iban": "DE89370400440532013000", + "issuance": "2012-01-01", + "reference": "0042", + "scan-sepa-mandate": "" + }, + "mitgliederdaten": { + "bis": "", + "mitgliedsbeitrag": "30", + "scan-antrag": "", + "schliessberechtigung": "Ja", + "spendenbeitrag": "0", + "status": "V", + "von": "2012-01-01" + }, + "stammdaten": { + "address_code": "39104", + "address_country": "DE", + "address_label": "Max Hackerberg\nLeibnizstr. 32\n39104 Magdeburg", + "address_locality": "Magdeburg", + "address_region": "", + "address_street": "Leibnizstr. 32", + "birth_date": "1970-01-01", + "birth_location": "Hackstadt", + "email": "max.hackerberg@netz39.de", + "fullname": "Max Hackerberg", + "nickname": "maxH", + "pgp-key": "", + "ssh-key": "" + }, + "timestamp": "2020-03-25T23:58:11", + "id": "" +} \ No newline at end of file -- 2.45.2 From 50e2a3f1dc127b082f34018b48cb3758b04c9c17 Mon Sep 17 00:00:00 2001 From: David Kilias Date: Mon, 4 Jan 2021 22:01:24 +0100 Subject: [PATCH 15/19] properly close file contexts --- test.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/test.py b/test.py index 0aa7420..8916983 100644 --- a/test.py +++ b/test.py @@ -58,7 +58,8 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") def test_valid_entity(self): - entity_file = json.load(open('test_cases/valid/valid.json','r')) + with open('test_cases/valid/valid.json','r') af f: + entity_file = json.load(f) response = self.fetch('/v0/validate', method='POST', @@ -72,7 +73,8 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertTrue(validation_result['valid'], "Validation result is expected to be valid==true") def test_invalid_iban(self): - entity_file = json.load(open('test_cases/invalid/invalid_iban.json','r')) + with open('test_cases/invalid/invalid_iban.json','r') af f: + entity_file = json.load(f) response = self.fetch('/v0/validate', method='POST', @@ -86,23 +88,25 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") def test_missing_id(self): - entity_file = json.load(open('test_cases/invalid/missing_id.json','r')) + with open('test_cases/invalid/missing_id.json','r') af f: + entity_file = json.load(f) - response = self.fetch('/v0/validate', - method='POST', - body=json.dumps(entity_file)) - - self.assertEqual(200, response.code, "Validation must always return 200") + response = self.fetch('/v0/validate', + method='POST', + body=json.dumps(entity_file)) + + self.assertEqual(200, response.code, "Validation must always return 200") - validation_result = json.loads(response.body.decode()) + validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") - self.assertIn('id', validation_result['errors']) + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + self.assertIn('id', validation_result['errors']) def test_invalid_id(self): - entity_file = json.load(open('test_cases/invalid/invalid_id.json','r')) - + with open('test_cases/invalid/invalid_id.json','r') af f: + entity_file = json.load(f) + response = self.fetch('/v0/validate', method='POST', body=json.dumps(entity_file)) -- 2.45.2 From 7609c08cb41c6a398cc7cdaf6392aabe085fee9a Mon Sep 17 00:00:00 2001 From: David Kilias Date: Mon, 4 Jan 2021 22:14:36 +0100 Subject: [PATCH 16/19] pycodestype (pep8) fixes --- app.py | 12 ++++-- test.py | 90 +++++++++++++++++++++++++---------------- validation_functions.py | 7 +++- 3 files changed, 70 insertions(+), 39 deletions(-) diff --git a/app.py b/app.py index 26f16af..75c66ca 100644 --- a/app.py +++ b/app.py @@ -35,10 +35,12 @@ class HealthHandler(tornado.web.RequestHandler, metaclass=ABCMeta): # if not available, try git if v is None: try: - v = subprocess.check_output(["git", "describe", "--always", "--dirty"], + v = subprocess.check_output(["git", "describe", "--always", + "--dirty"], cwd=os.path.dirname(__file__)).strip().decode() except subprocess.CalledProcessError as e: - print("Checking git version lead to non-null return code ", e.returncode) + print("Checking git version lead to non-null return code ", + e.returncode) return v @@ -50,7 +52,8 @@ class HealthHandler(tornado.web.RequestHandler, metaclass=ABCMeta): health['git-version'] = self.git_version health['timestamp'] = isodate.datetime_isoformat(datetime.now()) - health['uptime'] = isodate.duration_isoformat(datetime.now() - startup_timestamp) + health['uptime'] = isodate.duration_isoformat(datetime.now() + - startup_timestamp) self.set_header("Content-Type", "application/json") self.write(json.dumps(health, indent=4)) @@ -61,7 +64,8 @@ class Oas3Handler(tornado.web.RequestHandler, metaclass=ABCMeta): def get(self): self.set_header("Content-Type", "text/plain") # This is the proposed content type, - # but browsers like Firefox try to download instead of display the content + # but browsers like Firefox try to download instead of + # display the content # self.set_header("Content-Type", "text/vnd.yml") with open('OAS3.yml', 'r') as f: oas3 = f.read() diff --git a/test.py b/test.py index 8916983..1f67411 100644 --- a/test.py +++ b/test.py @@ -22,11 +22,16 @@ class TestBaseAPI(tornado.testing.AsyncHTTPTestCase): health = json.loads(response.body.decode()) - self.assertIn('api-version', health, msg="api-version is not provided by health endpoint") - self.assertEqual("v0", health['api-version'], msg="API version should be v0") - self.assertIn('git-version', health, msg="git-version is not provided by health endpoint") - self.assertIn('timestamp', health, msg="timestamp is not provided by health endpoint") - self.assertIn('uptime', health, msg="uptime is not provided by health endpoint") + self.assertIn('api-version', health, + msg="api-version is not provided by health endpoint") + self.assertEqual("v0", health['api-version'], + msg="API version should be v0") + self.assertIn('git-version', health, + msg="git-version is not provided by health endpoint") + self.assertIn('timestamp', health, + msg="timestamp is not provided by health endpoint") + self.assertIn('uptime', health, + msg="uptime is not provided by health endpoint") def test_oas3(self): response = self.fetch('/v0/oas3', @@ -35,7 +40,8 @@ class TestBaseAPI(tornado.testing.AsyncHTTPTestCase): # check contents against local OAS3.yml with open('OAS3.yml') as oas3f: - self.assertEqual(response.body.decode(), oas3f.read(), "OAS3 content differs from spec file!") + self.assertEqual(response.body.decode(), oas3f.read(), + "OAS3 content differs from spec file!") class TestValidation(tornado.testing.AsyncHTTPTestCase): @@ -50,74 +56,90 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): method='POST', body=json.dumps(entity)) - self.assertEqual(200, response.code, "Validation must always return 200") + self.assertEqual(200, response.code, + "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + self.assertIn('valid', validation_result, + "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], + "Validation result is expected to be valid==false") def test_valid_entity(self): - with open('test_cases/valid/valid.json','r') af f: + with open('test_cases/valid/valid.json', 'r') af f: entity_file = json.load(f) response = self.fetch('/v0/validate', method='POST', body=json.dumps(entity_file)) - - self.assertEqual(200, response.code, "Validation must always return 200") + + self.assertEqual(200, response.code, + "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") - self.assertTrue(validation_result['valid'], "Validation result is expected to be valid==true") + self.assertIn('valid', validation_result, + "Key 'valid' expected in validation result") + self.assertTrue(validation_result['valid'], + "Validation result is expected to be valid==true") def test_invalid_iban(self): - with open('test_cases/invalid/invalid_iban.json','r') af f: + with open('test_cases/invalid/invalid_iban.json', 'r') af f: entity_file = json.load(f) response = self.fetch('/v0/validate', method='POST', body=json.dumps(entity_file)) - - self.assertEqual(200, response.code, "Validation must always return 200") + + self.assertEqual(200, response.code, + "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + self.assertIn('valid', validation_result, + "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], + "Validation result is expected to be valid==false") def test_missing_id(self): - with open('test_cases/invalid/missing_id.json','r') af f: + with open('test_cases/invalid/missing_id.json', 'r') af f: entity_file = json.load(f) response = self.fetch('/v0/validate', - method='POST', - body=json.dumps(entity_file)) - - self.assertEqual(200, response.code, "Validation must always return 200") + method='POST', + body=json.dumps(entity_file)) + + self.assertEqual(200, response.code, + "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + self.assertIn('valid', validation_result, + "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], + "Validation result is expected to be valid==false") self.assertIn('id', validation_result['errors']) def test_invalid_id(self): - with open('test_cases/invalid/invalid_id.json','r') af f: + with open('test_cases/invalid/invalid_id.json', 'r') af f: entity_file = json.load(f) - + response = self.fetch('/v0/validate', - method='POST', - body=json.dumps(entity_file)) - - self.assertEqual(200, response.code, "Validation must always return 200") + method='POST', + body=json.dumps(entity_file)) + + self.assertEqual(200, response.code, + "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") + self.assertIn('valid', validation_result, + "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], + "Validation result is expected to be valid==false") self.assertIn('id', validation_result['errors']) + if __name__ == "__main__": unittest.main() diff --git a/validation_functions.py b/validation_functions.py index 9e5b29a..e31d498 100644 --- a/validation_functions.py +++ b/validation_functions.py @@ -2,6 +2,7 @@ from schwifty import IBAN, BIC import datetime from validator_collection import validators, errors + def valid_iban(field, value, error): try: IBAN(value) @@ -9,6 +10,7 @@ def valid_iban(field, value, error): except ValueError: error(field, 'not a valid IBAN') + def valid_bic(field, value, error): try: BIC(value) @@ -16,6 +18,7 @@ def valid_bic(field, value, error): except ValueError: error(field, 'not a valid BIC') + def iso_date(field, value, error): try: datetime.datetime.strptime(value, "%Y-%m-%d") @@ -23,6 +26,7 @@ def iso_date(field, value, error): except ValueError: error(field, 'not a valid ISO 8601 date') + def valid_money_amount(field, value, error): try: # value is string, check formatting by parsing as float @@ -31,9 +35,10 @@ def valid_money_amount(field, value, error): except (ValueError, TypeError): error(field, 'not a valid money value') + def valid_email(field, value, error): try: validators.email(value) return True except errors.InvalidEmailError: - error(field, 'not a valid email') \ No newline at end of file + error(field, 'not a valid email') -- 2.45.2 From afee6c62a6ea09361f8c0cc8eaa05ff2cc10166f Mon Sep 17 00:00:00 2001 From: David Kilias Date: Mon, 4 Jan 2021 23:16:00 +0100 Subject: [PATCH 17/19] revert linebreaks, no need to limit to 80 char lines --- app.py | 9 +++------ test.py | 60 +++++++++++++++++++-------------------------------------- 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/app.py b/app.py index 75c66ca..6f07684 100644 --- a/app.py +++ b/app.py @@ -35,8 +35,7 @@ class HealthHandler(tornado.web.RequestHandler, metaclass=ABCMeta): # if not available, try git if v is None: try: - v = subprocess.check_output(["git", "describe", "--always", - "--dirty"], + v = subprocess.check_output(["git", "describe", "--always", "--dirty"], cwd=os.path.dirname(__file__)).strip().decode() except subprocess.CalledProcessError as e: print("Checking git version lead to non-null return code ", @@ -52,8 +51,7 @@ class HealthHandler(tornado.web.RequestHandler, metaclass=ABCMeta): health['git-version'] = self.git_version health['timestamp'] = isodate.datetime_isoformat(datetime.now()) - health['uptime'] = isodate.duration_isoformat(datetime.now() - - startup_timestamp) + health['uptime'] = isodate.duration_isoformat(datetime.now() - startup_timestamp) self.set_header("Content-Type", "application/json") self.write(json.dumps(health, indent=4)) @@ -64,8 +62,7 @@ class Oas3Handler(tornado.web.RequestHandler, metaclass=ABCMeta): def get(self): self.set_header("Content-Type", "text/plain") # This is the proposed content type, - # but browsers like Firefox try to download instead of - # display the content + # but browsers like Firefox try to download instead of display the content # self.set_header("Content-Type", "text/vnd.yml") with open('OAS3.yml', 'r') as f: oas3 = f.read() diff --git a/test.py b/test.py index 1f67411..ba6e905 100644 --- a/test.py +++ b/test.py @@ -22,16 +22,11 @@ class TestBaseAPI(tornado.testing.AsyncHTTPTestCase): health = json.loads(response.body.decode()) - self.assertIn('api-version', health, - msg="api-version is not provided by health endpoint") - self.assertEqual("v0", health['api-version'], - msg="API version should be v0") - self.assertIn('git-version', health, - msg="git-version is not provided by health endpoint") - self.assertIn('timestamp', health, - msg="timestamp is not provided by health endpoint") - self.assertIn('uptime', health, - msg="uptime is not provided by health endpoint") + self.assertIn('api-version', health, msg="api-version is not provided by health endpoint") + self.assertEqual("v0", health['api-version'], msg="API version should be v0") + self.assertIn('git-version', health, msg="git-version is not provided by health endpoint") + self.assertIn('timestamp', health, msg="timestamp is not provided by health endpoint") + self.assertIn('uptime', health, msg="uptime is not provided by health endpoint") def test_oas3(self): response = self.fetch('/v0/oas3', @@ -40,8 +35,7 @@ class TestBaseAPI(tornado.testing.AsyncHTTPTestCase): # check contents against local OAS3.yml with open('OAS3.yml') as oas3f: - self.assertEqual(response.body.decode(), oas3f.read(), - "OAS3 content differs from spec file!") + self.assertEqual(response.body.decode(), oas3f.read(), "OAS3 content differs from spec file!") class TestValidation(tornado.testing.AsyncHTTPTestCase): @@ -56,15 +50,12 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): method='POST', body=json.dumps(entity)) - self.assertEqual(200, response.code, - "Validation must always return 200") + self.assertEqual(200, response.code, "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, - "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], - "Validation result is expected to be valid==false") + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") def test_valid_entity(self): with open('test_cases/valid/valid.json', 'r') af f: @@ -74,15 +65,12 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): method='POST', body=json.dumps(entity_file)) - self.assertEqual(200, response.code, - "Validation must always return 200") + self.assertEqual(200, response.code, "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, - "Key 'valid' expected in validation result") - self.assertTrue(validation_result['valid'], - "Validation result is expected to be valid==true") + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertTrue(validation_result['valid'], "Validation result is expected to be valid==true") def test_invalid_iban(self): with open('test_cases/invalid/invalid_iban.json', 'r') af f: @@ -92,15 +80,12 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): method='POST', body=json.dumps(entity_file)) - self.assertEqual(200, response.code, - "Validation must always return 200") + self.assertEqual(200, response.code, "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, - "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], - "Validation result is expected to be valid==false") + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") def test_missing_id(self): with open('test_cases/invalid/missing_id.json', 'r') af f: @@ -110,15 +95,12 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): method='POST', body=json.dumps(entity_file)) - self.assertEqual(200, response.code, - "Validation must always return 200") + self.assertEqual(200, response.code, "Validation must always return 200") validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, - "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], - "Validation result is expected to be valid==false") + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") self.assertIn('id', validation_result['errors']) def test_invalid_id(self): @@ -134,10 +116,8 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): validation_result = json.loads(response.body.decode()) - self.assertIn('valid', validation_result, - "Key 'valid' expected in validation result") - self.assertFalse(validation_result['valid'], - "Validation result is expected to be valid==false") + self.assertIn('valid', validation_result, "Key 'valid' expected in validation result") + self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") self.assertIn('id', validation_result['errors']) -- 2.45.2 From 68686fd3cab67c0cc56635f466fd476a8fc766b7 Mon Sep 17 00:00:00 2001 From: David Kilias Date: Mon, 4 Jan 2021 23:17:30 +0100 Subject: [PATCH 18/19] fotgot one --- app.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app.py b/app.py index 6f07684..26f16af 100644 --- a/app.py +++ b/app.py @@ -38,8 +38,7 @@ class HealthHandler(tornado.web.RequestHandler, metaclass=ABCMeta): v = subprocess.check_output(["git", "describe", "--always", "--dirty"], cwd=os.path.dirname(__file__)).strip().decode() except subprocess.CalledProcessError as e: - print("Checking git version lead to non-null return code ", - e.returncode) + print("Checking git version lead to non-null return code ", e.returncode) return v -- 2.45.2 From 54e052f24d2e83d36e0f68fb5024824d33b6e5c2 Mon Sep 17 00:00:00 2001 From: Stefan Haun Date: Tue, 5 Jan 2021 14:32:12 +0100 Subject: [PATCH 19/19] Fix syntax errors in test cases --- test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test.py b/test.py index ba6e905..708df04 100644 --- a/test.py +++ b/test.py @@ -58,7 +58,7 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") def test_valid_entity(self): - with open('test_cases/valid/valid.json', 'r') af f: + with open('test_cases/valid/valid.json', 'r') as f: entity_file = json.load(f) response = self.fetch('/v0/validate', @@ -73,7 +73,7 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertTrue(validation_result['valid'], "Validation result is expected to be valid==true") def test_invalid_iban(self): - with open('test_cases/invalid/invalid_iban.json', 'r') af f: + with open('test_cases/invalid/invalid_iban.json', 'r') as f: entity_file = json.load(f) response = self.fetch('/v0/validate', @@ -88,7 +88,7 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertFalse(validation_result['valid'], "Validation result is expected to be valid==false") def test_missing_id(self): - with open('test_cases/invalid/missing_id.json', 'r') af f: + with open('test_cases/invalid/missing_id.json', 'r') as f: entity_file = json.load(f) response = self.fetch('/v0/validate', @@ -104,7 +104,7 @@ class TestValidation(tornado.testing.AsyncHTTPTestCase): self.assertIn('id', validation_result['errors']) def test_invalid_id(self): - with open('test_cases/invalid/invalid_id.json', 'r') af f: + with open('test_cases/invalid/invalid_id.json', 'r') as f: entity_file = json.load(f) response = self.fetch('/v0/validate', -- 2.45.2