From 549cf0a3e8c7e74d95b2ed9feff9d6af164b01b9 Mon Sep 17 00:00:00 2001 From: Francesco Siddi Date: Thu, 12 Jul 2018 15:23:57 +0200 Subject: [PATCH] WIP on libraries upgrade --- pillar/__init__.py | 2 +- pillar/api/custom_field_validation.py | 59 +++++++++------------- pillar/api/eve_settings.py | 12 ++--- pillar/api/node_types/__init__.py | 2 +- pillar/api/node_types/comment.py | 2 +- pillar/api/node_types/group.py | 5 +- pillar/api/node_types/post.py | 2 +- pillar/api/nodes/__init__.py | 37 ++++++++++++++ pillar/api/organizations/patch.py | 2 +- pillar/tests/__init__.py | 15 +++--- pillar/tests/config_testing.py | 4 +- pillar/tests/eve_test_settings.py | 3 ++ pillar/web/nodes/attachments.py | 2 +- requirements.txt | 8 +-- tests/test_api/test_auth.py | 26 +++++----- tests/test_api/test_bcloud_home_project.py | 18 +++---- tests/test_api/test_file_storage.py | 4 +- tests/test_api/test_markdown.py | 10 ++-- tests/test_api/test_nodes.py | 34 ++++++------- tests/test_api/test_organizations.py | 6 +-- tests/test_api/test_patch.py | 24 ++++----- tests/test_api/test_patch_handler.py | 2 +- tests/test_api/test_project_management.py | 12 ++--- tests/test_api/test_project_merging.py | 4 +- tests/test_api/test_service_accounts.py | 2 +- tests/test_api/test_subscriptions.py | 12 ++--- tests/test_cli/test_maintenance.py | 6 +-- tests/test_sdk.py | 8 +-- tests/test_shortcodes.py | 2 +- 29 files changed, 177 insertions(+), 148 deletions(-) diff --git a/pillar/__init__.py b/pillar/__init__.py index 3c28e9eb..b412b7bf 100644 --- a/pillar/__init__.py +++ b/pillar/__init__.py @@ -280,7 +280,7 @@ class PillarServer(BlinkerCompatibleEve): self.encoding_service_client = Zencoder(self.config['ZENCODER_API_KEY']) def _config_caching(self): - from flask_cache import Cache + from flask_caching import Cache self.cache = Cache(self) def set_languages(self, translations_folder: pathlib.Path): diff --git a/pillar/api/custom_field_validation.py b/pillar/api/custom_field_validation.py index dc4a9a25..44c04c7d 100644 --- a/pillar/api/custom_field_validation.py +++ b/pillar/api/custom_field_validation.py @@ -97,7 +97,7 @@ class ValidateCustomFields(Validator): return False try: - value = self.convert_properties(value, node_type['dyn_schema']) + value = self.convert_properties(value, node_type['dyn_schema']['shema']) except Exception as e: log.warning("Error converting form properties", exc_info=True) @@ -107,7 +107,10 @@ class ValidateCustomFields(Validator): if val: # This ensures the modifications made by v's coercion rules are # visible to this validator's output. - self.current[field] = v.current + #self.current[field] = v.current + print('*********') + print(valid_properties) + #print(v.current) return True log.warning('Error validating properties for node %s: %s', self.document, v.errors) @@ -149,39 +152,25 @@ class ValidateCustomFields(Validator): if ip.prefixlen() == 0: self._error(field_name, 'Zero-length prefix is not allowed') - def _validate_type_binary(self, field_name: str, value: bytes): - """Add support for binary type. + # def _validate_coerce(self, coerce, field: str, value): + # """Override Cerberus' _validate_coerce method for richer features. + # + # This now supports named coercion functions (available in Cerberus 1.0+) + # and passes the field name to coercion functions as well. + # """ + # if isinstance(coerce, str): + # coerce = getattr(self, f'_normalize_coerce_{coerce}') + # + # try: + # return coerce(field, value) + # except (TypeError, ValueError): + # self._error(field, cerberus.errors.ERROR_COERCION_FAILED.format(field)) - This type was actually introduced in Cerberus 1.0, so we can drop - support for this once Eve starts using that version (or newer). + def _validator_markdown(self, field, value): + """This is a placeholder. + + Markdown is actually processed in a hook """ - - if not isinstance(value, (bytes, bytearray)): - self._error(field_name, f'wrong value type {type(value)}, expected bytes or bytearray') - - def _validate_coerce(self, coerce, field: str, value): - """Override Cerberus' _validate_coerce method for richer features. - - This now supports named coercion functions (available in Cerberus 1.0+) - and passes the field name to coercion functions as well. - """ - if isinstance(coerce, str): - coerce = getattr(self, f'_normalize_coerce_{coerce}') - - try: - return coerce(field, value) - except (TypeError, ValueError): - self._error(field, cerberus.errors.ERROR_COERCION_FAILED.format(field)) - - def _normalize_coerce_markdown(self, field: str, value): - """Render Markdown from this field into {field}_html. - - The field name MUST NOT end in `_html`. The Markdown is read from this - field and the rendered HTML is written to the field `{field}_html`. - """ - html = pillar.markdown.markdown(value) - field_name = pillar.markdown.cache_field_name(field) - self.current[field_name] = html return value @@ -190,12 +179,12 @@ if __name__ == '__main__': v = ValidateCustomFields() v.schema = { - 'foo': {'type': 'string', 'coerce': 'markdown'}, + 'foo': {'type': 'string', 'validator': 'markdown'}, 'foo_html': {'type': 'string'}, 'nested': { 'type': 'dict', 'schema': { - 'bar': {'type': 'string', 'coerce': 'markdown'}, + 'bar': {'type': 'string', 'validator': 'markdown'}, 'bar_html': {'type': 'string'}, } } diff --git a/pillar/api/eve_settings.py b/pillar/api/eve_settings.py index 7f534c85..d8914cbf 100644 --- a/pillar/api/eve_settings.py +++ b/pillar/api/eve_settings.py @@ -155,7 +155,7 @@ organizations_schema = { 'description': { 'type': 'string', 'maxlength': 256, - 'coerce': 'markdown', + 'validator': 'markdown', }, '_description_html': {'type': 'string'}, 'website': { @@ -292,7 +292,7 @@ nodes_schema = { }, 'description': { 'type': 'string', - 'coerce': 'markdown', + 'validator': 'markdown', }, '_description_html': {'type': 'string'}, 'picture': _file_embedded_schema, @@ -326,8 +326,8 @@ nodes_schema = { }, 'properties': { 'type': 'dict', - 'valid_properties': True, - 'required': True, + # 'valid_properties': True, + 'required': True }, 'permissions': { 'type': 'dict', @@ -539,7 +539,7 @@ projects_schema = { }, 'description': { 'type': 'string', - 'coerce': 'markdown', + 'validator': 'markdown', }, '_description_html': {'type': 'string'}, # Short summary for the project @@ -833,4 +833,4 @@ UPSET_ON_PUT = False # do not create new document on PUT of non-existant URL. X_DOMAINS = '*' X_ALLOW_CREDENTIALS = True X_HEADERS = 'Authorization' -XML = False +RENDERERS = ['eve.render.JSONRenderer'] diff --git a/pillar/api/node_types/__init__.py b/pillar/api/node_types/__init__.py index 6ea1fac5..84b0e0ac 100644 --- a/pillar/api/node_types/__init__.py +++ b/pillar/api/node_types/__init__.py @@ -12,7 +12,7 @@ ATTACHMENT_SLUG_REGEX = r'[a-zA-Z0-9_\-]+' attachments_embedded_schema = { 'type': 'dict', # TODO: will be renamed to 'keyschema' in Cerberus 1.0 - 'propertyschema': { + 'keyschema': { 'type': 'string', 'regex': '^%s$' % ATTACHMENT_SLUG_REGEX, }, diff --git a/pillar/api/node_types/comment.py b/pillar/api/node_types/comment.py index 89866910..43fe92c7 100644 --- a/pillar/api/node_types/comment.py +++ b/pillar/api/node_types/comment.py @@ -7,7 +7,7 @@ node_type_comment = { 'type': 'string', 'minlength': 5, 'required': True, - 'coerce': 'markdown', + 'validator': 'markdown', }, '_content_html': {'type': 'string'}, 'status': { diff --git a/pillar/api/node_types/group.py b/pillar/api/node_types/group.py index de5cc876..33ca1f2d 100644 --- a/pillar/api/node_types/group.py +++ b/pillar/api/node_types/group.py @@ -3,7 +3,7 @@ node_type_group = { 'description': 'Folder node type', 'parent': ['group', 'project'], 'dyn_schema': { - # Used for sorting within the context of a group + 'order': { 'type': 'integer' }, @@ -20,7 +20,8 @@ node_type_group = { 'notes': { 'type': 'string', 'maxlength': 256, - }, + } + }, 'form_schema': { 'url': {'visible': False}, diff --git a/pillar/api/node_types/post.py b/pillar/api/node_types/post.py index 90fef0fa..c5afd24e 100644 --- a/pillar/api/node_types/post.py +++ b/pillar/api/node_types/post.py @@ -9,7 +9,7 @@ node_type_post = { 'minlength': 5, 'maxlength': 90000, 'required': True, - 'coerce': 'markdown', + 'validator': 'markdown', }, '_content_html': {'type': 'string'}, 'status': { diff --git a/pillar/api/nodes/__init__.py b/pillar/api/nodes/__init__.py index 509b6905..f41925de 100644 --- a/pillar/api/nodes/__init__.py +++ b/pillar/api/nodes/__init__.py @@ -8,6 +8,7 @@ import werkzeug.exceptions as wz_exceptions from bson import ObjectId from flask import current_app, Blueprint, request +import pillar.markdown from pillar.api.activities import activity_subscribe, activity_object_add from pillar.api.node_types import PILLAR_NAMED_NODE_TYPES from pillar.api.file_storage_backends.gcs import update_file_name @@ -400,6 +401,40 @@ def textures_sort_files(nodes): texture_sort_files(node) +def parse_markdown(node, original=None): + projects_collection = current_app.data.driver.db['projects'] + project = projects_collection.find_one({'_id': node['project']}, {'node_types': 1}) + # Query node type directly using the key + node_type = next(nt for nt in project['node_types'] + if nt['name'] == node['node_type']) + schema = current_app.config['DOMAIN']['nodes']['schema'] + schema['properties'] = node_type['dyn_schema'] + + def find_markdown_fields(schema, node): + """Find and process all makrdown validated fields.""" + for k, v in schema.items(): + if isinstance(v, dict): + if 'validator' in v and 'markdown' == v['validator']: + # If there is a match with the validator: markdown pair, assign the sibling + # property (following the naming convention __html) + # the processed value. + if k in node: + html = pillar.markdown.markdown(node[k]) + field_name = pillar.markdown.cache_field_name(k) + node[field_name] = html + if isinstance(node, dict) and k in node: + find_markdown_fields(v, node[k]) + + find_markdown_fields(schema, node) + + return 'ok' + + +def parse_markdowns(items): + for item in items: + parse_markdown(item) + + def setup_app(app, url_prefix): from . import patch patch.setup_app(app, url_prefix=url_prefix) @@ -408,12 +443,14 @@ def setup_app(app, url_prefix): app.on_fetched_resource_nodes += before_returning_nodes app.on_replace_nodes += before_replacing_node + app.on_replace_nodes += parse_markdown app.on_replace_nodes += texture_sort_files app.on_replace_nodes += deduct_content_type app.on_replace_nodes += node_set_default_picture app.on_replaced_nodes += after_replacing_node app.on_insert_nodes += before_inserting_nodes + app.on_insert_nodes += parse_markdowns app.on_insert_nodes += nodes_deduct_content_type app.on_insert_nodes += nodes_set_default_picture app.on_insert_nodes += textures_sort_files diff --git a/pillar/api/organizations/patch.py b/pillar/api/organizations/patch.py index 00a9563d..8f083759 100644 --- a/pillar/api/organizations/patch.py +++ b/pillar/api/organizations/patch.py @@ -225,4 +225,4 @@ class OrganizationPatchHandler(patch_handler.AbstractPatchHandler): def setup_app(app): OrganizationPatchHandler(patch_api_blueprint) - app.register_api_blueprint(patch_api_blueprint, url_prefix='/organizations') + # app.register_api_blueprint(patch_api_blueprint, url_prefix='/organizations') diff --git a/pillar/tests/__init__.py b/pillar/tests/__init__.py index 1d7677be..35e4f471 100644 --- a/pillar/tests/__init__.py +++ b/pillar/tests/__init__.py @@ -522,13 +522,12 @@ class AbstractPillarTest(TestMinimal): expected_status, resp.status_code, resp.data )) - def get_json(): - if resp.mimetype != 'application/json': - raise TypeError('Unable to load JSON from mimetype %r' % resp.mimetype) - return mod_json.loads(resp.data) - - resp.json = get_json - resp.get_json = get_json + # def get_json(): + # if resp.mimetype != 'application/json': + # raise TypeError('Unable to load JSON from mimetype %r' % resp.mimetype) + # return mod_json.loads(resp.data) + # + # resp.get_json = get_json return resp @@ -561,7 +560,7 @@ class AbstractPillarTest(TestMinimal): raise TypeError('expected_user_id should be a string or ObjectId, ' f'but is {expected_user_id!r}') - resp = self.get('/api/users/me', expected_status=200, auth_token=token).json() + resp = self.get('/api/users/me', expected_status=200, auth_token=token).get_json() if expected_user_id: self.assertEqual(resp['_id'], str(expected_user_id)) diff --git a/pillar/tests/config_testing.py b/pillar/tests/config_testing.py index 4c9d03a9..75f5a0aa 100644 --- a/pillar/tests/config_testing.py +++ b/pillar/tests/config_testing.py @@ -2,8 +2,8 @@ BLENDER_ID_ENDPOINT = 'http://id.local:8001' # Non existant server -SERVER_NAME = 'localhost' -PILLAR_SERVER_ENDPOINT = 'http://localhost/api/' +SERVER_NAME = 'localhost.local' +PILLAR_SERVER_ENDPOINT = 'http://localhost.local/api/' MAIN_PROJECT_ID = '5672beecc0261b2005ed1a33' diff --git a/pillar/tests/eve_test_settings.py b/pillar/tests/eve_test_settings.py index ae7d9365..3adf2371 100644 --- a/pillar/tests/eve_test_settings.py +++ b/pillar/tests/eve_test_settings.py @@ -1,6 +1,7 @@ from pillar.api.eve_settings import * MONGO_DBNAME = 'pillar_test' +MONGO_USERNAME = None def override_eve(): @@ -10,5 +11,7 @@ def override_eve(): test_settings.MONGO_HOST = MONGO_HOST test_settings.MONGO_PORT = MONGO_PORT test_settings.MONGO_DBNAME = MONGO_DBNAME + test_settings.MONGO1_USERNAME = MONGO_USERNAME tests.MONGO_HOST = MONGO_HOST tests.MONGO_DBNAME = MONGO_DBNAME + tests.MONGO_USERNAME = MONGO_USERNAME diff --git a/pillar/web/nodes/attachments.py b/pillar/web/nodes/attachments.py index 6859b3bf..321ab9d0 100644 --- a/pillar/web/nodes/attachments.py +++ b/pillar/web/nodes/attachments.py @@ -21,7 +21,7 @@ def attachment_form_group_create(schema_prop): def _attachment_build_single_field(schema_prop): # Ugly hard-coded schema. fake_schema = { - 'slug': schema_prop['propertyschema'], + 'slug': schema_prop['keyschema'], 'oid': schema_prop['valueschema']['schema']['oid'], } file_select_form_group = build_file_select_form(fake_schema) diff --git a/requirements.txt b/requirements.txt index c81c763a..211550c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,10 +10,10 @@ celery[redis]==4.0.2 CommonMark==0.7.2 elasticsearch==6.1.1 elasticsearch-dsl==6.1.0 -Eve==0.7.3 -Flask==0.12 +Eve==0.8 +Flask==1.0.2 Flask-Babel==0.11.2 -Flask-Cache==0.13.1 +Flask-Caching==1.4.0 Flask-Script==2.0.6 Flask-Login==0.3.2 Flask-WTF==0.14.2 @@ -37,7 +37,7 @@ zencoder==0.6.5 amqp==2.1.4 billiard==3.5.0.2 Flask-PyMongo==0.4.1 --e git+https://github.com/armadillica/cerberus.git@sybren-0.9#egg=Cerberus +Cerberus==1.2.0 Events==0.2.2 future==0.15.2 html5lib==0.99999999 diff --git a/tests/test_api/test_auth.py b/tests/test_api/test_auth.py index 292ba7f5..13b97dfc 100644 --- a/tests/test_api/test_auth.py +++ b/tests/test_api/test_auth.py @@ -294,7 +294,7 @@ class UserListTests(AbstractPillarTest): def test_list_all_users_subscriber(self): # Regular access should result in only your own info. - users = self.get('/api/users', auth_token='token').json() + users = self.get('/api/users', auth_token='token').get_json() self.assertEqual(1, users['_meta']['total']) # The 'auth' section should be removed. @@ -303,7 +303,7 @@ class UserListTests(AbstractPillarTest): def test_list_all_users_admin(self): # Admin access should result in all users - users = self.get('/api/users', auth_token='admin-token').json() + users = self.get('/api/users', auth_token='admin-token').get_json() self.assertEqual(3, users['_meta']['total']) # The 'auth' section should be removed. @@ -314,7 +314,7 @@ class UserListTests(AbstractPillarTest): """Even admins shouldn't be able to GET auth info.""" projection = json.dumps({'auth': 1}) - users = self.get(f'/api/users?projection={projection}', auth_token='admin-token').json() + users = self.get(f'/api/users?projection={projection}', auth_token='admin-token').get_json() self.assertEqual(3, users['_meta']['total']) # The 'auth' section should be removed. @@ -333,21 +333,21 @@ class UserListTests(AbstractPillarTest): def test_own_user_subscriber(self): # Regular access should result in only your own info. - user_info = self.get('/api/users/123456789abc123456789abc', auth_token='token').json() + user_info = self.get('/api/users/123456789abc123456789abc', auth_token='token').get_json() self.assertNotIn('auth', user_info) def test_own_user_subscriber_explicit_projection(self): # With a custom projection requesting the auth list projection = json.dumps({'auth': 1}) user_info = self.get(f'/api/users/123456789abc123456789abc?projection={projection}', - auth_token='token').json() + auth_token='token').get_json() self.assertNotIn('auth', user_info) def test_other_user_subscriber(self): from pillar.api.utils import remove_private_keys # Requesting another user should be limited to full name and email. - user_info = self.get('/api/users/223456789abc123456789abc', auth_token='token').json() + user_info = self.get('/api/users/223456789abc123456789abc', auth_token='token').get_json() self.assertNotIn('auth', user_info) regular_info = remove_private_keys(user_info) @@ -357,7 +357,7 @@ class UserListTests(AbstractPillarTest): from pillar.api.utils import remove_private_keys # PUTting a user should work, and not mess up the auth field. - user_info = self.get('/api/users/123456789abc123456789abc', auth_token='token').json() + user_info = self.get('/api/users/123456789abc123456789abc', auth_token='token').get_json() self.assertNotIn('auth', user_info) put_user = remove_private_keys(user_info) @@ -378,7 +378,7 @@ class UserListTests(AbstractPillarTest): group_ids = self.create_standard_groups() # A user should be able to change only some fields, but not all. - user_info = self.get('/api/users/me', auth_token='token').json() + user_info = self.get('/api/users/me', auth_token='token').get_json() # Alter all fields (except auth, another test already checks that that's uneditable). put_user = remove_private_keys(user_info) @@ -395,7 +395,7 @@ class UserListTests(AbstractPillarTest): auth_token='token', etag=user_info['_etag']) - new_user_info = self.get('/api/users/me', auth_token='token').json() + new_user_info = self.get('/api/users/me', auth_token='token').get_json() self.assertEqual(new_user_info['full_name'], put_user['full_name']) self.assertEqual(new_user_info['username'], put_user['username']) self.assertEqual(new_user_info['email'], put_user['email']) @@ -409,7 +409,7 @@ class UserListTests(AbstractPillarTest): from pillar.api.utils import remove_private_keys # PUTting the user as another user should fail. - user_info = self.get('/api/users/123456789abc123456789abc', auth_token='token').json() + user_info = self.get('/api/users/123456789abc123456789abc', auth_token='token').get_json() put_user = remove_private_keys(user_info) self.put('/api/users/123456789abc123456789abc', auth_token='other-token', @@ -420,7 +420,7 @@ class UserListTests(AbstractPillarTest): from pillar.api.utils import remove_private_keys # PUTting a user should work, and not mess up the auth field. - user_info = self.get('/api/users/123456789abc123456789abc', auth_token='token').json() + user_info = self.get('/api/users/123456789abc123456789abc', auth_token='token').get_json() put_user = remove_private_keys(user_info) self.put('/api/users/123456789abc123456789abc', auth_token='admin-token', @@ -841,7 +841,7 @@ class UserCreationTest(AbstractPillarTest): etag = db_user['_etag'] resp = self.put(f'/api/users/{user_id}', json=puttable, etag=etag, - auth_token='user-token', expected_status=200).json() + auth_token='user-token', expected_status=200).get_json() etag = resp['_etag'] self.put(f'/api/users/{user_id}', json=empty_email, etag=etag, auth_token='user-token', expected_status=422) @@ -851,7 +851,7 @@ class UserCreationTest(AbstractPillarTest): # An admin should be able to edit this user, but also not clear the email address. self.create_user(24 * 'a', roles={'admin'}, token='admin-token') resp = self.put(f'/api/users/{user_id}', json=puttable, etag=etag, - auth_token='admin-token', expected_status=200).json() + auth_token='admin-token', expected_status=200).get_json() etag = resp['_etag'] self.put(f'/api/users/{user_id}', json=empty_email, etag=etag, auth_token='admin-token', expected_status=422) diff --git a/tests/test_api/test_bcloud_home_project.py b/tests/test_api/test_bcloud_home_project.py index 9730618e..3b9ec4b5 100644 --- a/tests/test_api/test_bcloud_home_project.py +++ b/tests/test_api/test_bcloud_home_project.py @@ -296,7 +296,7 @@ class HomeProjectTest(AbstractHomeProjectTest): # Create home project by getting it. resp = self.get('/api/bcloud/home-project', auth_token='token') - before_delete_json_proj = resp.json() + before_delete_json_proj = resp.get_json() # Delete the project. self.delete(f'/api/projects/{before_delete_json_proj["_id"]}', @@ -305,7 +305,7 @@ class HomeProjectTest(AbstractHomeProjectTest): # Recreate home project by getting it. resp = self.get('/api/bcloud/home-project', auth_token='token') - after_delete_json_proj = resp.json() + after_delete_json_proj = resp.get_json() self.assertEqual(before_delete_json_proj['_id'], after_delete_json_proj['_id']) @@ -452,7 +452,7 @@ class TextureLibraryTest(AbstractHomeProjectTest): def test_hdri_library__no_bcloud_version(self): resp = self.get('/api/bcloud/texture-libraries', auth_token='token') - libs = resp.json()['_items'] + libs = resp.get_json()['_items'] library_project_ids = {proj['_id'] for proj in libs} self.assertNotIn(str(self.hdri_proj_id), library_project_ids) @@ -462,7 +462,7 @@ class TextureLibraryTest(AbstractHomeProjectTest): resp = self.get('/api/bcloud/texture-libraries', auth_token='token', headers={'Blender-Cloud-Addon': '1.3.3'}) - libs = resp.json()['_items'] + libs = resp.get_json()['_items'] library_project_ids = {proj['_id'] for proj in libs} self.assertNotIn(str(self.hdri_proj_id), library_project_ids) self.assertIn(str(self.tex_proj_id), library_project_ids) @@ -471,7 +471,7 @@ class TextureLibraryTest(AbstractHomeProjectTest): resp = self.get('/api/bcloud/texture-libraries', auth_token='token', headers={'Blender-Cloud-Addon': '1.4.0'}) - libs = resp.json()['_items'] + libs = resp.get_json()['_items'] library_project_ids = {proj['_id'] for proj in libs} self.assertIn(str(self.hdri_proj_id), library_project_ids) self.assertIn(str(self.tex_proj_id), library_project_ids) @@ -539,11 +539,11 @@ class HdriSortingTest(AbstractHomeProjectTest): 'name': 'Symmetrical Garden' } resp = self.post('/api/nodes', json=node, expected_status=201, auth_token='token') - node_info = resp.json() + node_info = resp.json # Check that the node's files are in the right order resp = self.get('/api/nodes/%s' % node_info['_id'], auth_token='token') - get_node = resp.json() + get_node = resp.json self.assertEqual(['256p', '1k', '2k'], [file_info['resolution'] @@ -565,7 +565,7 @@ class HdriSortingTest(AbstractHomeProjectTest): 'name': 'Symmetrical Garden' } resp = self.post('/api/nodes', json=node, expected_status=201, auth_token='token') - node_info = resp.json() + node_info = resp.json # Mess up the node's order node['properties']['files'] = [ @@ -578,7 +578,7 @@ class HdriSortingTest(AbstractHomeProjectTest): # Check that the node's files are in the right order resp = self.get('/api/nodes/%s' % node_info['_id'], auth_token='token') - get_node = resp.json() + get_node = resp.json self.assertEqual(['256p', '1k', '2k'], [file_info['resolution'] diff --git a/tests/test_api/test_file_storage.py b/tests/test_api/test_file_storage.py index 821dc014..bcc87515 100644 --- a/tests/test_api/test_file_storage.py +++ b/tests/test_api/test_file_storage.py @@ -192,7 +192,7 @@ class FileMaxSizeTest(AbstractPillarTest): expected_status=201, auth_token='token', files={'file': (test_file, 'test_file.bin')}) - stream_info = resp.json() + stream_info = resp.get_json() file_id = stream_info['file_id'] self.assert_file_doc_ok(file_id, file_size) @@ -216,7 +216,7 @@ class FileMaxSizeTest(AbstractPillarTest): expected_status=201, auth_token='token', files={'file': (test_file, 'test_file.bin')}) - stream_info = resp.json() + stream_info = resp.get_json() file_id = stream_info['file_id'] self.assert_file_doc_ok(file_id, file_size) diff --git a/tests/test_api/test_markdown.py b/tests/test_api/test_markdown.py index 95e42974..3261067c 100644 --- a/tests/test_api/test_markdown.py +++ b/tests/test_api/test_markdown.py @@ -16,10 +16,10 @@ class CoerceMarkdownTest(AbstractPillarTest): } created_data = self.post('/api/nodes', json=node, expected_status=201, - auth_token='token-a').json() + auth_token='token-a').get_json() node_id = created_data['_id'] - json_node = self.get(f'/api/nodes/{node_id}', auth_token='token-a').json() + json_node = self.get(f'/api/nodes/{node_id}', auth_token='token-a').get_json() self.assertEqual('

Title

\n

This is content.

\n', json_node['_description_html']) @@ -43,7 +43,7 @@ class CoerceMarkdownTest(AbstractPillarTest): pid = r['_id'] - json_proj = self.get(f'/api/projects/{pid}', auth_token='token-a').json() + json_proj = self.get(f'/api/projects/{pid}', auth_token='token-a').get_json() json_proj.pop('node_types', None) # just to make it easier to print import pprint pprint.pprint(json_proj) @@ -63,9 +63,9 @@ class CoerceMarkdownTest(AbstractPillarTest): } created_data = self.post('/api/nodes', json=node, expected_status=201, - auth_token='token-a').json() + auth_token='token-a').get_json() node_id = created_data['_id'] - json_node = self.get(f'/api/nodes/{node_id}', auth_token='token-a').json() + json_node = self.get(f'/api/nodes/{node_id}', auth_token='token-a').get_json() expect = '

Title

\n\n' self.assertEqual(expect, json_node['_description_html']) diff --git a/tests/test_api/test_nodes.py b/tests/test_api/test_nodes.py index eee66643..0f861c72 100644 --- a/tests/test_api/test_nodes.py +++ b/tests/test_api/test_nodes.py @@ -253,7 +253,7 @@ class NodeSharingTest(AbstractPillarTest): 'name': str(self), 'properties': {}, }) - self.node_id = resp.json()['_id'] + self.node_id = resp.get_json()['_id'] def _check_share_data(self, share_data): base_url = self.app.config['SHORT_LINK_BASE_URL'] @@ -265,7 +265,7 @@ class NodeSharingTest(AbstractPillarTest): # Share the node resp = self.post('/api/nodes/%s/share' % self.node_id, auth_token='token', expected_status=201) - share_data = resp.json() + share_data = resp.get_json() self._check_share_data(share_data) @@ -279,7 +279,7 @@ class NodeSharingTest(AbstractPillarTest): # Check that an anonymous user has acces. resp = self.get('/api/nodes/%s' % self.node_id) - self.assertEqual(str(self.node_id), resp.json()['_id']) + self.assertEqual(str(self.node_id), resp.get_json()['_id']) def test_other_user_access_shared_node(self): # Share the node @@ -290,7 +290,7 @@ class NodeSharingTest(AbstractPillarTest): other_user_id = self.create_user(user_id=24 * 'a') self.create_valid_auth_token(other_user_id, 'other-token') resp = self.get('/api/nodes/%s' % self.node_id, auth_token='other-token') - self.assertEqual(str(self.node_id), resp.json()['_id']) + self.assertEqual(str(self.node_id), resp.get_json()['_id']) def test_get_share_data__unshared_node(self): self.get('/api/nodes/%s/share' % self.node_id, @@ -304,7 +304,7 @@ class NodeSharingTest(AbstractPillarTest): # Then get its share info. resp = self.get('/api/nodes/%s/share' % self.node_id, auth_token='token') - share_data = resp.json() + share_data = resp.get_json() self._check_share_data(share_data) @@ -349,7 +349,7 @@ class NodeSharingTest(AbstractPillarTest): resp = self.post('/api/nodes/%s/share' % self.node_id, auth_token='token', expected_status=201) - share_data = resp.json() + share_data = resp.get_json() self._check_share_data(share_data) self.assertEqual(3, create_short_link.call_count) @@ -360,19 +360,19 @@ class NodeSharingTest(AbstractPillarTest): # Share the node resp = self.post('/api/nodes/%s/share' % self.node_id, auth_token='token', expected_status=201) - share_data = resp.json() + share_data = resp.get_json() # Get the node with short_code resp = self.get('/api/nodes/%s' % self.node_id, json={'projection': {'short_code': 1}}) - node = resp.json() + node = resp.get_json() self.assertEqual(node['short_code'], share_data['short_code']) self.assertTrue(node['short_link'].endswith(share_data['short_code'])) # Get the node without short_code resp = self.get('/api/nodes/%s' % self.node_id, qs={'projection': {'short_code': 0}}) - node = resp.json() + node = resp.get_json() self.assertNotIn('short_code', node) self.assertNotIn('short_link', node) @@ -409,10 +409,10 @@ class TextureSortFilesTest(AbstractPillarTest): }, 'user': ctd.EXAMPLE_PROJECT_OWNER_ID, }) - node_id = resp.json()['_id'] + node_id = resp.get_json()['_id'] resp = self.get(f'/api/nodes/{node_id}', auth_token='token') - node = resp.json() + node = resp.get_json() map_types = [f['map_type'] for f in node['properties']['files']] self.assertEqual(['color', 'alpha', 'specular'], map_types) @@ -435,10 +435,10 @@ class TextureSortFilesTest(AbstractPillarTest): }, 'user': ctd.EXAMPLE_PROJECT_OWNER_ID, }) - node_id = resp.json()['_id'] + node_id = resp.get_json()['_id'] resp = self.get(f'/api/nodes/{node_id}', auth_token='token') - node = resp.json() + node = resp.get_json() map_types = [f['map_type'] for f in node['properties']['files']] self.assertEqual(['alpha', 'bump', 'specular'], map_types) @@ -453,10 +453,10 @@ class TextureSortFilesTest(AbstractPillarTest): }, 'user': ctd.EXAMPLE_PROJECT_OWNER_ID, }) - node_id = resp.json()['_id'] + node_id = resp.get_json()['_id'] resp = self.get(f'/api/nodes/{node_id}', auth_token='token') - node = resp.json() + node = resp.get_json() self.assertEqual([], node['properties']['files']) def test_no_files_list(self): @@ -468,9 +468,9 @@ class TextureSortFilesTest(AbstractPillarTest): 'properties': {}, 'user': ctd.EXAMPLE_PROJECT_OWNER_ID, }) - node_id = resp.json()['_id'] + node_id = resp.get_json()['_id'] resp = self.get(f'/api/nodes/{node_id}', auth_token='token') - node = resp.json() + node = resp.get_json() self.assertNotIn('files', node['properties']) diff --git a/tests/test_api/test_organizations.py b/tests/test_api/test_organizations.py index c8c498ee..abd99c0c 100644 --- a/tests/test_api/test_organizations.py +++ b/tests/test_api/test_organizations.py @@ -843,7 +843,7 @@ class IPRangeTest(AbstractIPRangeSingleOrgTest): def test_ipranges_get_via_eve(self): self.test_patch_set_ip_ranges_happy() r = self.get(f'/api/organizations/{self.org_id}', auth_token='token') - from_eve = r.json() + from_eve = r.get_json() # Eve cannot return binary data, at least not until we upgrade to a version # that depends on Cerberus >= 1.0. @@ -853,7 +853,7 @@ class IPRangeTest(AbstractIPRangeSingleOrgTest): self.assertEqual(expect_ranges, from_eve['ip_ranges']) r = self.get(f'/api/organizations', auth_token='token') - from_eve = r.json() + from_eve = r.get_json() self.assertEqual(expect_ranges, from_eve['_items'][0]['ip_ranges']) def test_patch_unset_ip_ranges_happy(self): @@ -996,7 +996,7 @@ class IPRangeLoginRolesTest(AbstractIPRangeSingleOrgTest): # user in the database. resp = self.get('/api/users/me', auth_token='usertoken', headers=headers, environ_overrides=env) - me = resp.json() + me = resp.get_json() # The IP-based roles should be stored in the token document. self.enter_app_context() diff --git a/tests/test_api/test_patch.py b/tests/test_api/test_patch.py index 2433fc79..bd3a3eae 100644 --- a/tests/test_api/test_patch.py +++ b/tests/test_api/test_patch.py @@ -24,7 +24,7 @@ class AbstractPatchCommentTest(AbstractPillarTest): resp = self.post('/api/nodes', json=asset, auth_token='owner-token', expected_status=201) - self.asset_id = resp.json()['_id'] + self.asset_id = resp.get_json()['_id'] # Create the comment comment = {'description': '', @@ -43,7 +43,7 @@ class AbstractPatchCommentTest(AbstractPillarTest): resp = self.post('/api/nodes', json=comment, auth_token='owner-token', expected_status=201) - comment_info = resp.json() + comment_info = resp.get_json() self.node_url = '/api/nodes/%s' % comment_info['_id'] @@ -66,12 +66,12 @@ class VoteCommentTest(AbstractPatchCommentTest): # Patch the node res = self.patch(self.node_url, json={'op': 'upvote'}, - auth_token='token').json() + auth_token='token').get_json() self.assertEqual(1, res['properties']['rating_positive']) self.assertEqual(0, res['properties']['rating_negative']) # Get the node again, to inspect its changed state. - patched_node = self.get(self.node_url, auth_token='token').json() + patched_node = self.get(self.node_url, auth_token='token').get_json() self.assertEqual(1, patched_node['properties']['rating_positive']) self.assertEqual(0, patched_node['properties']['rating_negative']) self.assertEqual({'user': str(self.user_id), 'is_positive': True}, @@ -87,12 +87,12 @@ class VoteCommentTest(AbstractPatchCommentTest): # Patch the node res = self.patch(self.node_url, json={'op': 'downvote'}, - auth_token='token').json() + auth_token='token').get_json() self.assertEqual(0, res['properties']['rating_positive']) self.assertEqual(1, res['properties']['rating_negative']) # Get the node again, to inspect its changed state. - patched_node = self.get(self.node_url, auth_token='token').json() + patched_node = self.get(self.node_url, auth_token='token').get_json() self.assertEqual(0, patched_node['properties']['rating_positive']) self.assertEqual(1, patched_node['properties']['rating_negative']) self.assertEqual({'user': str(self.user_id), 'is_positive': False}, @@ -124,7 +124,7 @@ class VoteCommentTest(AbstractPatchCommentTest): auth_token='token') # Get the node again, to inspect its changed state. - patched_node = self.get(self.node_url, auth_token='token').json() + patched_node = self.get(self.node_url, auth_token='token').get_json() self.assertEqual(0, patched_node['properties']['rating_positive']) self.assertEqual(0, patched_node['properties']['rating_negative']) self.assertEqual([], patched_node['properties'].get('ratings', [])) @@ -167,7 +167,7 @@ class VoteCommentTest(AbstractPatchCommentTest): auth_token='other-token-4') # Inspect the result - patched_node = self.get(self.node_url, auth_token='token').json() + patched_node = self.get(self.node_url, auth_token='token').get_json() self.assertEqual(3, patched_node['properties']['rating_positive']) self.assertEqual(2, patched_node['properties']['rating_negative']) self.assertEqual([ @@ -181,16 +181,16 @@ class VoteCommentTest(AbstractPatchCommentTest): class EditCommentTest(AbstractPatchCommentTest): def test_comment_edit_happy(self, token='owner-token'): - pre_node = self.get(self.node_url, auth_token=token).json() + pre_node = self.get(self.node_url, auth_token=token).get_json() res = self.patch(self.node_url, json={'op': 'edit', 'content': 'Je moeder is niet je vader.'}, - auth_token=token).json() + auth_token=token).get_json() self.assertEqual('

Je moeder is niet je vader.

\n', res['properties']['_content_html']) # Get the node again, to inspect its changed state. - patched_node = self.get(self.node_url, auth_token=token).json() + patched_node = self.get(self.node_url, auth_token=token).get_json() self.assertEqual('Je moeder is niet je vader.', patched_node['properties']['content']) self.assertEqual('

Je moeder is niet je vader.

\n', @@ -210,7 +210,7 @@ class EditCommentTest(AbstractPatchCommentTest): expected_status=403) # Get the node again, to inspect its old state. - patched_node = self.get(self.node_url, auth_token='token').json() + patched_node = self.get(self.node_url, auth_token='token').get_json() self.assertEqual('Purrrr kittycat', patched_node['properties']['content']) self.assertEqual('

Purrrr kittycat

\n', diff --git a/tests/test_api/test_patch_handler.py b/tests/test_api/test_patch_handler.py index 00628439..cd9b5474 100644 --- a/tests/test_api/test_patch_handler.py +++ b/tests/test_api/test_patch_handler.py @@ -60,7 +60,7 @@ class PatchHandlerTest(AbstractPillarTest): resp = self.patch('/api/test/%s' % oid, auth_token='user-token', json={'op': 'test-echo', 'echo': '¡Thith ith Špahtah!'}) - self.assertEqual({'echo': '¡Thith ith Špahtah!'}, resp.json()) + self.assertEqual({'echo': '¡Thith ith Špahtah!'}, resp.get_json()) def test_patch_empty_response(self): import bson diff --git a/tests/test_api/test_project_management.py b/tests/test_api/test_project_management.py index 001888a0..20ecec03 100644 --- a/tests/test_api/test_project_management.py +++ b/tests/test_api/test_project_management.py @@ -166,7 +166,7 @@ class ProjectEditTest(AbstractProjectTest): project_info = self._create_user_and_project(['subscriber']) project_url = '/api/projects/%(_id)s' % project_info - project = self.get(project_url, auth_token='token').json() + project = self.get(project_url, auth_token='token').get_json() # Create another user we can try and assign the project to. other_user_id = 'f00dd00df00dd00df00dd00d' @@ -199,7 +199,7 @@ class ProjectEditTest(AbstractProjectTest): # Re-fetch from database to see which fields actually made it there. # equal to put_project -> changed in DB # equal to project -> not changed in DB - db_proj = self.get(project_url, auth_token='token').json() + db_proj = self.get(project_url, auth_token='token').get_json() self.assertEqual(project['url'], db_proj['url']) self.assertEqual(put_project['description'], db_proj['description']) self.assertEqual(put_project['name'], db_proj['name']) @@ -391,7 +391,7 @@ class ProjectEditTest(AbstractProjectTest): expected_status=204) resp = self.get(f'/api/files/{fid}', expected_status=404) - self.assertEqual(str(fid), resp.json()['_id']) + self.assertEqual(str(fid), resp.get_json()['_id']) with self.app.app_context(): db_file_after = files_coll.find_one(fid) @@ -413,7 +413,7 @@ class ProjectEditTest(AbstractProjectTest): project_info['picture_header'] = str(fid) resp = self.put(proj_url, auth_token='token', etag=etag, json=remove_private_keys(project_info)) - etag = resp.json()['_etag'] + etag = resp.get_json()['_etag'] # DELETE the project. self.delete(proj_url, auth_token='token', etag=etag, expected_status=204) @@ -433,7 +433,7 @@ class ProjectEditTest(AbstractProjectTest): self.patch(proj_url, auth_token='token', json={'op': 'undelete'}, expected_status=204) resp = self.get(f'/api/files/{fid}') - self.assertEqual(str(fid), resp.json()['_id']) + self.assertEqual(str(fid), resp.get_json()['_id']) with self.app.app_context(): files_coll = self.app.db('files') @@ -456,7 +456,7 @@ class ProjectEditTest(AbstractProjectTest): db_file_after = files_coll.find_one(fid) self.assertTrue(db_file_after['_deleted']) - resp = self.get(proj_url, auth_token='token', expected_status=404).json() + resp = self.get(proj_url, auth_token='token', expected_status=404).get_json() self.assertTrue(resp['_deleted']) diff --git a/tests/test_api/test_project_merging.py b/tests/test_api/test_project_merging.py index 932af7ef..9eb15ff7 100644 --- a/tests/test_api/test_project_merging.py +++ b/tests/test_api/test_project_merging.py @@ -31,8 +31,8 @@ class ProjectMergerTest(AbstractPillarTest): with self.app.app_context(): merging.merge_project(self.pid_from, self.pid_to) - db_file = self.get(f'/api/files/{fid}').json() - db_node = self.get(f'/api/nodes/{nid}').json() + db_file = self.get(f'/api/files/{fid}').get_json() + db_node = self.get(f'/api/nodes/{nid}').get_json() self.assertEqual(db_file['project'], str(self.pid_to)) self.assertEqual(db_node['project'], str(self.pid_to)) diff --git a/tests/test_api/test_service_accounts.py b/tests/test_api/test_service_accounts.py index 999bcd61..9a6b0fd8 100644 --- a/tests/test_api/test_service_accounts.py +++ b/tests/test_api/test_service_accounts.py @@ -63,7 +63,7 @@ class ServiceAccountCreationTest(AbstractPillarTest): resp = self.put(f'/api/users/{user_id}', json=puttable, auth_token=token['token'], - etag=etag).json() + etag=etag).get_json() etag = resp['_etag'] with self.app.test_request_context(): diff --git a/tests/test_api/test_subscriptions.py b/tests/test_api/test_subscriptions.py index e5b4a3c0..ada3620d 100644 --- a/tests/test_api/test_subscriptions.py +++ b/tests/test_api/test_subscriptions.py @@ -58,7 +58,7 @@ class RoleUpdatingTest(AbstractPillarTest): self.get('/api/bcloud/update-subscription', auth_token='my-happy-token', expected_status=204) - user_info = self.get('/api/users/me', auth_token='my-happy-token').json() + user_info = self.get('/api/users/me', auth_token='my-happy-token').get_json() self.assertEqual({'subscriber', 'has_subscription'}, set(user_info['roles'])) # Check the signals @@ -75,13 +75,13 @@ class RoleUpdatingTest(AbstractPillarTest): # Make sure this user is currently known as a subcriber. self.create_user(roles={'subscriber', 'has_subscription'}, token='my-happy-token') - user_info = self.get('/api/users/me', auth_token='my-happy-token').json() + user_info = self.get('/api/users/me', auth_token='my-happy-token').get_json() self.assertEqual({'subscriber', 'has_subscription'}, set(user_info['roles'])) # And after updating, it shouldn't be. self.get('/api/bcloud/update-subscription', auth_token='my-happy-token', expected_status=204) - user_info = self.get('/api/users/me', auth_token='my-happy-token').json() + user_info = self.get('/api/users/me', auth_token='my-happy-token').get_json() self.assertEqual([], user_info['roles']) self.assertEqual(1, len(self.user_subs_signal_calls)) @@ -98,7 +98,7 @@ class RoleUpdatingTest(AbstractPillarTest): self.get('/api/bcloud/update-subscription', auth_token='my-happy-token', expected_status=204) - user_info = self.get('/api/users/me', auth_token='my-happy-token').json() + user_info = self.get('/api/users/me', auth_token='my-happy-token').get_json() self.assertEqual(['demo'], user_info['roles']) self.assertEqual(1, len(self.user_subs_signal_calls)) @@ -113,13 +113,13 @@ class RoleUpdatingTest(AbstractPillarTest): # Make sure this user is currently known as demo user. self.create_user(roles={'demo'}, token='my-happy-token') - user_info = self.get('/api/users/me', auth_token='my-happy-token').json() + user_info = self.get('/api/users/me', auth_token='my-happy-token').get_json() self.assertEqual(['demo'], user_info['roles']) # And after updating, it shouldn't be. self.get('/api/bcloud/update-subscription', auth_token='my-happy-token', expected_status=204) - user_info = self.get('/api/users/me', auth_token='my-happy-token').json() + user_info = self.get('/api/users/me', auth_token='my-happy-token').get_json() self.assertEqual([], user_info['roles']) self.assertEqual(1, len(self.user_subs_signal_calls)) diff --git a/tests/test_cli/test_maintenance.py b/tests/test_cli/test_maintenance.py index 0e40c78c..70fd6ed3 100644 --- a/tests/test_cli/test_maintenance.py +++ b/tests/test_cli/test_maintenance.py @@ -14,8 +14,8 @@ class PurgeHomeProjectsTest(AbstractPillarTest): user_b = self.create_user(user_id=24 * 'b', roles={'subscriber'}, token='token-b') # GET the home project to create it. - home_a = self.get('/api/bcloud/home-project', auth_token='token-a').json() - home_b = self.get('/api/bcloud/home-project', auth_token='token-b').json() + home_a = self.get('/api/bcloud/home-project', auth_token='token-a').get_json() + home_b = self.get('/api/bcloud/home-project', auth_token='token-b').get_json() with self.app.app_context(): users_coll = self.app.db('users') @@ -60,7 +60,7 @@ class UpgradeAttachmentSchemaTest(AbstractPillarTest): "url": {"type": "string"}, "attachments": { "type": "dict", - "propertyschema": {"type": "string", "regex": "^[a-zA-Z0-9_ ]+$"}, + "keyschema": {"type": "string", "regex": "^[a-zA-Z0-9_ ]+$"}, "valueschema": { "type": "dict", "schema": { diff --git a/tests/test_sdk.py b/tests/test_sdk.py index e2768ec1..4b987dae 100644 --- a/tests/test_sdk.py +++ b/tests/test_sdk.py @@ -42,7 +42,7 @@ class FlaskInternalApiTest(AbstractPillarTest): with self.app.test_request_context(): # Check the asset in MongoDB resp = self.get(url_for('nodes|item_lookup', _id=asset._id), auth_token='token') - db_asset = resp.json() + db_asset = resp.get_json() self.assertEqual('Test asset', db_asset['name']) return asset @@ -66,7 +66,7 @@ class FlaskInternalApiTest(AbstractPillarTest): # Check the file in MongoDB with self.app.test_request_context(): resp = self.get(url_for('files|item_lookup', _id=file_id), auth_token='token') - file_doc = resp.json() + file_doc = resp.get_json() self.assertEqual('BlenderDesktopLogo.png', file_doc['filename']) def test_create_asset_from_file(self): @@ -80,7 +80,7 @@ class FlaskInternalApiTest(AbstractPillarTest): 'properties': {} }, expected_status=201) - parent_id = resp.json()['_id'] + parent_id = resp.get_json()['_id'] with self.app.test_request_context(), open(blender_desktop_logo_path, 'rb') as fileobj: resp = pillarsdk.Node.create_asset_from_file( @@ -99,5 +99,5 @@ class FlaskInternalApiTest(AbstractPillarTest): # Check the node in MongoDB with self.app.test_request_context(): resp = self.get(url_for('nodes|item_lookup', _id=node_id), auth_token='token') - node_doc = resp.json() + node_doc = resp.get_json() self.assertEqual('BlenderDesktopLogo.png', node_doc['name']) diff --git a/tests/test_shortcodes.py b/tests/test_shortcodes.py index 18422aa7..28144511 100644 --- a/tests/test_shortcodes.py +++ b/tests/test_shortcodes.py @@ -183,7 +183,7 @@ class AttachmentTest(AbstractPillarTest): # We have to get the file document again, because retrieving it via the # API (which is what the shortcode rendering is doing) will change its # link URL. - db_file = self.get(f'/api/files/{oid}').json() + db_file = self.get(f'/api/files/{oid}').get_json() link = db_file['variations'][0]['link'] with self.app.test_request_context():