From bb4eeb0b1120db8c6f5eb3bb112f0a882f3e0271 Mon Sep 17 00:00:00 2001 From: Chunlin Zhang Date: Tue, 22 Oct 2019 15:31:57 +0800 Subject: [PATCH 1/4] tests: add 15 tests for apijson_put --- tests/demo/apps/apijson_demo/models.py | 4 + tests/demo/apps/apijson_demo/settings.ini | 10 +- tests/test.py | 209 ++++++++++++++++++++++ uliweb_apijson/apijson/views.py | 5 +- 4 files changed, 225 insertions(+), 3 deletions(-) diff --git a/tests/demo/apps/apijson_demo/models.py b/tests/demo/apps/apijson_demo/models.py index af73cf6..ef92a94 100644 --- a/tests/demo/apps/apijson_demo/models.py +++ b/tests/demo/apps/apijson_demo/models.py @@ -31,3 +31,7 @@ class Comment(Model): class PublicNotice(Model): date = Field(datetime.datetime, auto_now_add=True) content = Field(TEXT) + +class NoRequestTag(Model): + user_id = Reference("user") + content = Field(TEXT) diff --git a/tests/demo/apps/apijson_demo/settings.ini b/tests/demo/apps/apijson_demo/settings.ini index 82c0207..d9987d2 100644 --- a/tests/demo/apps/apijson_demo/settings.ini +++ b/tests/demo/apps/apijson_demo/settings.ini @@ -3,6 +3,7 @@ privacy = 'apijson_demo.models.Privacy' comment = 'apijson_demo.models.Comment' moment = 'apijson_demo.models.Moment' publicnotice = 'apijson_demo.models.PublicNotice' +norequesttag = 'apijson_demo.models.NoRequestTag' [APIJSON_MODELS] user = { @@ -42,7 +43,7 @@ publicnotice = { "GET" : { "roles" : ["OWNER","LOGIN","ADMIN","UNKNOWN"] }, "HEAD" : { "roles" : ["OWNER","LOGIN","ADMIN","UNKNOWN"] }, "POST" : { "roles" : ["OWNER","ADMIN"] }, - "PUT" : { "roles" : ["OWNER","ADMIN"] }, + "PUT" : { "roles" : ["OWNER","ADMIN","UNKNOWN"] }, "DELETE" : { "roles" : ["OWNER","ADMIN"] }, } @@ -68,5 +69,12 @@ comment = { "PUT" :{ "ADD":{"@role": "OWNER"}, "NECESSARY" : ["id","content"], + "DISALLOW" : ["user_id","to_id"], }, } + +publicnotice = { + "PUT" :{ + "NECESSARY" : ["id","content"], + } +} diff --git a/tests/test.py b/tests/test.py index 06e0de2..e906f17 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1024,4 +1024,213 @@ def test_apijson_put(): >>> d = json_loads(r.data) >>> print(d) {'code': 200, 'msg': 'success', 'moment': {'id': 1, 'code': 200, 'msg': 'success', 'count': 1}} + + >>> #apijson put, model not exists + >>> data ='''{ + ... "nonexist": { + ... "@role": "ADMIN", + ... "id": 1, + ... "content": "moment content after change 2" + ... }, + ... "@tag": "nonexist" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "model 'nonexist' not found"} + + >>> #apijson put, with a model no tag + >>> data ='''{ + ... "norequesttag": { + ... "user_id": 1, + ... "content": "test" + ... }, + ... "@tag": "norequesttag" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "tag 'norequesttag' not found"} + + >>> #apijson put, test ADD in put + >>> data ='''{ + ... "moment": { + ... "id": 2, + ... "content": "moment content after change" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': 'no permission'} + + >>> #apijson put, without id + >>> data ='''{ + ... "moment": { + ... "content": "moment content after change" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': 'id param needed'} + + >>> #apijson put, with id not integer + >>> data ='''{ + ... "moment": { + ... "id": "abc", + ... "content": "moment content after change" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "id 'abc' cannot convert to integer"} + + >>> #apijson put, with a id cannot find record + >>> data ='''{ + ... "moment": { + ... "id": 100, + ... "content": "moment content after change" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "cannot find record which id = '100'"} + + >>> #apijson put, with a role which have no permission + >>> data ='''{ + ... "moment": { + ... "@role": "LOGIN", + ... "id": 1, + ... "content": "moment content after change" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'moment' not accessible by role 'LOGIN'"} + + >>> #apijson put, with UNKNOWN role + >>> data ='''{ + ... "publicnotice": { + ... "@role": "UNKNOWN", + ... "id": 1, + ... "content": "content after change" + ... }, + ... "@tag": "publicnotice" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'publicnotice': {'id': 1, 'code': 200, 'msg': 'success', 'count': 1}} + + >>> #apijson put, OWNER but not login + >>> data ='''{ + ... "moment": { + ... "id": 1, + ... "content": "moment content after change" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'OWNER' need login user"} + + >>> #apijson put, with a role not permission + >>> data ='''{ + ... "moment": { + ... "@role": "superuser", + ... "id": 1, + ... "content": "moment content after change" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'moment' not accessible by role 'superuser'"} + + >>> #apijson put, with a field DISALLOWed + >>> data ='''{ + ... "comment": { + ... "id": 2, + ... "user_id": 2, + ... "content": "content after change" + ... }, + ... "@tag": "comment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "request 'comment' disallow 'user_id'"} + + + >>> #apijson put, without a field in NECESSARY + >>> data ='''{ + ... "moment": { + ... "id": 1 + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "request 'moment' have not necessary field 'content'"} + + >>> #apijson put, with a non exist field + >>> data ='''{ + ... "moment": { + ... "id": 1, + ... "content": "moment content after change", + ... "noexist": "test" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'moment' don't have field 'noexist'"} + + >>> #apijson put, and check get result + >>> data ='''{ + ... "moment": { + ... "id": 1, + ... "content": "check 789" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment': {'id': 1, 'code': 200, 'msg': 'success', 'count': 1}} + >>> data ='''{ + ... "moment":{ + ... "id": %s + ... } + ... }'''%(d['moment']['id']) + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment': {'user_id': 2, 'date': '2018-11-01 00:00:00', 'content': 'check 789', 'picture_list': '[]', 'id': 1}} + + >>> #apijson put, and check get result + >>> data ='''{ + ... "moment": { + ... "id": 1, + ... "content": "check 789" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': 'failed when updating, maybe no change', 'moment': {'id': 1, 'code': 400, 'msg': 'failed when updating, maybe no change', 'count': 0}} """ diff --git a/uliweb_apijson/apijson/views.py b/uliweb_apijson/apijson/views.py index 4542988..a79ba00 100644 --- a/uliweb_apijson/apijson/views.py +++ b/uliweb_apijson/apijson/views.py @@ -494,7 +494,7 @@ def _put_one(self,key,tag): return json({"code":400,"msg":"id '%s' cannot convert to integer"%(params.get("id"))}) obj = model.get(id_) if not obj: - return json({"code":400,"msg":"cannot find record id '%s'"%(id_)}) + return json({"code":400,"msg":"cannot find record which id = '%s'"%(id_)}) permission_check_ok = False model_PUT = model_setting.get("PUT") @@ -513,7 +513,7 @@ def _put_one(self,key,tag): permission_check_ok = True break else: - return json({"code":400,"msg":"need login user"}) + return json({"code":400,"msg":"'OWNER' need login user"}) elif role == "UNKNOWN": permission_check_ok = True break @@ -522,6 +522,7 @@ def _put_one(self,key,tag): permission_check_ok = True break + #current implementation won't run here, but keep for safe if not permission_check_ok: return json({"code":400,"msg":"no permission"}) From 6f8cadeb87ae02c2911327eae7e63fd611fd497d Mon Sep 17 00:00:00 2001 From: Chunlin Zhang Date: Tue, 22 Oct 2019 15:54:34 +0800 Subject: [PATCH 2/4] apison_post: modify to be same with original apijson, just return "id" and "count", not other fields --- tests/test.py | 3 +-- uliweb_apijson/apijson/views.py | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/test.py b/tests/test.py index e906f17..01e3dec 100644 --- a/tests/test.py +++ b/tests/test.py @@ -950,9 +950,8 @@ def test_apijson_post(): ... }''' >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("admin"), middlewares=[]) >>> d = json_loads(r.data) - >>> del d['moment']['date'] >>> print(d) - {'code': 200, 'msg': 'success', 'moment': {'user_id': 1, 'content': 'new moment for test', 'picture_list': ['http://static.oschina.net/uploads/user/48/96331_50.jpg'], 'id': 4, 'code': 200, 'message': 'success'}} + {'code': 200, 'msg': 'success', 'moment': {'id': 4, 'count': 1, 'code': 200, 'message': 'success'}} >>> #apijson post to a non exist model >>> data ='''{ diff --git a/uliweb_apijson/apijson/views.py b/uliweb_apijson/apijson/views.py index a79ba00..95e78b4 100644 --- a/uliweb_apijson/apijson/views.py +++ b/uliweb_apijson/apijson/views.py @@ -422,13 +422,12 @@ def _post_one(self,key,tag): obj = model(**params) ret = obj.save() - obj_dict = obj.to_dict(convert=False) - secret_fields = model_setting.get("secret_fields") - if secret_fields: - for k in secret_fields: - del obj_dict[k] + d = obj.to_dict(convert=False) + obj_dict = {} if ret: + obj_dict["id"] = d.get("id") + obj_dict["count"] = 1 obj_dict["code"] = 200 obj_dict["message"] = "success" else: From 8a266cf0e216ade14bfb3f7f3e67da107b7db54d Mon Sep 17 00:00:00 2001 From: Chunlin Zhang Date: Tue, 22 Oct 2019 17:42:41 +0800 Subject: [PATCH 3/4] tests: add 11 tests for apijson_delete --- tests/demo/apps/apijson_demo/settings.ini | 2 +- tests/test.py | 194 +++++++++++++++++++++- uliweb_apijson/apijson/views.py | 2 +- 3 files changed, 192 insertions(+), 6 deletions(-) diff --git a/tests/demo/apps/apijson_demo/settings.ini b/tests/demo/apps/apijson_demo/settings.ini index d9987d2..906cd61 100644 --- a/tests/demo/apps/apijson_demo/settings.ini +++ b/tests/demo/apps/apijson_demo/settings.ini @@ -44,7 +44,7 @@ publicnotice = { "HEAD" : { "roles" : ["OWNER","LOGIN","ADMIN","UNKNOWN"] }, "POST" : { "roles" : ["OWNER","ADMIN"] }, "PUT" : { "roles" : ["OWNER","ADMIN","UNKNOWN"] }, - "DELETE" : { "roles" : ["OWNER","ADMIN"] }, + "DELETE" : { "roles" : ["OWNER","ADMIN","UNKNOWN"] }, } [APIJSON_REQUESTS] diff --git a/tests/test.py b/tests/test.py index 01e3dec..df70569 100644 --- a/tests/test.py +++ b/tests/test.py @@ -2,12 +2,17 @@ from uliweb import manage from uliweb.manage import make_simple_application from json import loads as json_loads +from nose import with_setup -os.chdir('demo') +def setup(): + os.chdir('demo') -manage.call('uliweb syncdb -v') -manage.call('uliweb reset -v -y') -manage.call('uliweb dbinit -v') + manage.call('uliweb syncdb -v') + manage.call('uliweb reset -v -y') + manage.call('uliweb dbinit -v') + +def teardown(): + pass def pre_call_as(username): from uliweb import models @@ -17,6 +22,7 @@ def pre_call(request): request.user = user return pre_call +@with_setup(setup,teardown) def test_apijson_get(): """ >>> application = make_simple_application(project_dir='.') @@ -1233,3 +1239,183 @@ def test_apijson_put(): >>> print(d) {'code': 400, 'msg': 'failed when updating, maybe no change', 'moment': {'id': 1, 'code': 400, 'msg': 'failed when updating, maybe no change', 'count': 0}} """ + +def test_apijson_delete(): + """ + >>> application = make_simple_application(project_dir='.') + >>> handler = application.handler() + + >>> #apijson delete + >>> data ='''{ + ... "moment": { + ... "id": 1 + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment': {'id': 1, 'code': 200, 'message': 'success', 'count': 1}} + >>> data ='''{ + ... "moment": { + ... "id": 1 + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment': None} + + >>> #apijson delete, without @tag + >>> data ='''{ + ... "moment": { + ... "content": "new moment for test" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> data ='''{ + ... "moment": { + ... "id": %s + ... } + ... }'''%(d["moment"]["id"]) + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'tag' parameter is needed"} + + >>> #apijson delete, with non exist model + >>> data ='''{ + ... "nonexist": { + ... "id": 1 + ... }, + ... "@tag": "nonexist" + ... }''' + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "model 'nonexist' not found"} + + >>> #apijson delete, default to OWNER and delete other's record + >>> data ='''{ + ... "moment": { + ... "id": 2 + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': 'no permission'} + + >>> #apijson delete, without id + >>> data ='''{ + ... "moment": { + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': 'id param needed'} + + >>> #apijson delete, id not int + >>> data ='''{ + ... "moment": { + ... "id": "abc" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "id 'abc' cannot convert to integer"} + + >>> #apijson delete + >>> data ='''{ + ... "moment": { + ... "id": 100 + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "cannot find record id = '100'"} + + >>> #apijson delete, with a role having no permission + >>> data ='''{ + ... "moment": { + ... "content": "new moment for test" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> data ='''{ + ... "moment": { + ... "@role": "UNKNOWN", + ... "id": %s + ... }, + ... "@tag": "moment" + ... }'''%(d["moment"]["id"]) + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'moment' not accessible by role 'UNKNOWN'"} + + >>> #apijson delete, with OWNER but not login + >>> data ='''{ + ... "moment": { + ... "content": "new moment for test" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> data ='''{ + ... "moment": { + ... "id": %s + ... }, + ... "@tag": "moment" + ... }'''%(d["moment"]["id"]) + >>> r = handler.post('/apijson/delete', data=data, middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': 'need login user'} + + >>> #apijson delete, with UNKNOWN role + >>> data ='''{ + ... "publicnotice": { + ... "@role": "UNKNOWN", + ... "id": 1 + ... }, + ... "@tag": "publicnotice" + ... }''' + >>> r = handler.post('/apijson/delete', data=data, middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'publicnotice': {'id': 1, 'code': 200, 'message': 'success', 'count': 1}} + + >>> #apijson delete, with a role which have no permission + >>> data ='''{ + ... "moment": { + ... "content": "new moment for test" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> data ='''{ + ... "moment": { + ... "@role": "superuser", + ... "id": %s + ... }, + ... "@tag": "moment" + ... }'''%(d["moment"]["id"]) + >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'moment' not accessible by role 'superuser'"} + """ diff --git a/uliweb_apijson/apijson/views.py b/uliweb_apijson/apijson/views.py index 95e78b4..3008055 100644 --- a/uliweb_apijson/apijson/views.py +++ b/uliweb_apijson/apijson/views.py @@ -617,7 +617,7 @@ def _delete_one(self,key,tag): return json({"code":400,"msg":"id '%s' cannot convert to integer"%(params.get("id"))}) obj = model.get(id_) if not obj: - return json({"code":400,"msg":"cannot find record id '%s'"%(id_)}) + return json({"code":400,"msg":"cannot find record id = '%s'"%(id_)}) permission_check_ok = False DELETE = model_setting.get("DELETE") From 1b303bf5fc5875d72c52c45ef56b672da40c9d28 Mon Sep 17 00:00:00 2001 From: Chunlin Zhang Date: Tue, 22 Oct 2019 17:56:47 +0800 Subject: [PATCH 4/4] bump version to 0.2.1; update CHANGELOG --- CHANGELOG.md | 11 +++++++++++ uliweb_apijson/__init__.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e87a008..4e73204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +- 0.2.1(2019-10-22) + - **apison-post: modify to be same with original apijson, just return "id" and "count", not other fields** + - apijson-table: fix key and title problem in viewedit_items + - apijson-viewedit: fix @role + - apijson-table: fix apijson-put and apijson-delete @role not work problem + - doc: add difference in tag parameter in apijson post/put + - **tests: add 97 test cases, and fix bugs found by tests** + - fix "NameError: name 'log' is not defined" + - fix py2/3 compatible + - fix issue #4 :"NameError: name 'UliwebError' is not defined" + - apijson-table: add hook_add, for customizing the add action in apijson-table component - 0.2.0(2019-09-17) - setup: add fix utf8 for reading file - **fix json response structure to be same as original apijson api (java)** diff --git a/uliweb_apijson/__init__.py b/uliweb_apijson/__init__.py index 7dd6033..e823231 100644 --- a/uliweb_apijson/__init__.py +++ b/uliweb_apijson/__init__.py @@ -1,4 +1,4 @@ -__version__ = '0.2.0' +__version__ = '0.2.1' __url__ = 'https://github.com/zhangchunlin/uliweb-apijson' __author__ = 'Chunlin Zhang' __email__ = 'zhangchunlin@gmail.com'