diff --git a/demo/apps/apijson_demo/settings.ini b/demo/apps/apijson_demo/settings.ini index 1bf78f9..56c0258 100644 --- a/demo/apps/apijson_demo/settings.ini +++ b/demo/apps/apijson_demo/settings.ini @@ -31,6 +31,17 @@ comment = { } [APIJSON_REQUESTS] +user = { + "POST" :{ + "ADD":{"@role": "ADMIN"}, + "DISALLOW" : ["id"], + "NECESSARY" : ["username","nickname"], + }, + "PUT" :{ + "ADD":{"@role": "OWNER"}, + "NECESSARY" : ["id"], + }, +} moment = { "POST" :{ "ADD":{"@role": "OWNER"}, diff --git a/demo/apps/tables/settings.ini b/demo/apps/tables/settings.ini index 3cf9440..4712b17 100644 --- a/demo/apps/tables/settings.ini +++ b/demo/apps/tables/settings.ini @@ -1,4 +1,7 @@ [APIJSON_TABLES] +user = { + "editable" : "auto", +} moment = { "editable" : "auto", "table_fields" : [ diff --git a/tests/demo/apps/apijson_demo/dbinit.py b/tests/demo/apps/apijson_demo/dbinit.py index 08eb1cc..f66a7b2 100644 --- a/tests/demo/apps/apijson_demo/dbinit.py +++ b/tests/demo/apps/apijson_demo/dbinit.py @@ -15,25 +15,25 @@ "username": "admin", "nickname": "Administrator", "email": "admin@localhost", - "date_join": "2018-1-1", + "date_join": "2018-1-1 0:0:0", }, { "username": "usera", "nickname": "User A", "email": "usera@localhost", - "date_join": "2018-2-2", + "date_join": "2018-2-2 0:0:0", }, { "username": "userb", "nickname": "User B", "email": "userb@localhost", - "date_join": "2018-3-3", + "date_join": "2018-3-3 0:0:0", }, { "username": "userc", "nickname": "User C", "email": "userc@localhost", - "date_join": "2018-4-4", + "date_join": "2018-4-4 0:0:0", }, ] diff --git a/tests/demo/apps/apijson_demo/settings.ini b/tests/demo/apps/apijson_demo/settings.ini index c321e87..82c0207 100644 --- a/tests/demo/apps/apijson_demo/settings.ini +++ b/tests/demo/apps/apijson_demo/settings.ini @@ -10,7 +10,7 @@ user = { "secret_fields" : ["password"], "GET" : { "roles" : ["LOGIN","ADMIN","OWNER"] }, "HEAD" : { "roles" : ["LOGIN","ADMIN","OWNER"] }, - "POST" : { "roles" : ["ADMIN"] }, + #"POST" : { "roles" : ["ADMIN"] }, #remove for test case "PUT" : { "roles" : ["ADMIN","OWNER"] }, "DELETE" : { "roles" : ["ADMIN"] }, } @@ -63,7 +63,7 @@ comment = { "POST" :{ "ADD" :{"@role": "OWNER"}, "DISALLOW" : ["id"], - "NECESSARY" : ["content"] + "NECESSARY" : ["moment_id","content"] }, "PUT" :{ "ADD":{"@role": "OWNER"}, diff --git a/tests/test.py b/tests/test.py index 741b6b8..06e0de2 100644 --- a/tests/test.py +++ b/tests/test.py @@ -244,7 +244,7 @@ def test_apijson_get(): >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) >>> d = json_loads(r.data) >>> print(d) - {'code': 200, 'msg': 'success', '[]': [{'user': {'username': 'admin', 'nickname': 'Administrator', 'email': 'admin@localhost', 'is_superuser': True, 'last_login': None, 'date_join': '2018-11-01 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 1}}, {'user': {'username': 'usera', 'nickname': 'User A', 'email': 'usera@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-02-02 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 2}}, {'user': {'username': 'userb', 'nickname': 'User B', 'email': 'userb@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-03-03 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 3}}, {'user': {'username': 'userc', 'nickname': 'User C', 'email': 'userc@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-04-04 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 4}}]} + {'code': 200, 'msg': 'success', '[]': [{'user': {'username': 'admin', 'nickname': 'Administrator', 'email': 'admin@localhost', 'is_superuser': True, 'last_login': None, 'date_join': '2018-01-01 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 1}}, {'user': {'username': 'usera', 'nickname': 'User A', 'email': 'usera@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-02-02 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 2}}, {'user': {'username': 'userb', 'nickname': 'User B', 'email': 'userb@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-03-03 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 3}}, {'user': {'username': 'userc', 'nickname': 'User C', 'email': 'userc@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-04-04 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 4}}]} >>> #query array >>> data ='''{ @@ -326,7 +326,7 @@ def test_apijson_get(): >>> 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', '[]': [{'user': {'username': 'admin', 'nickname': 'Administrator', 'email': 'admin@localhost', 'is_superuser': True, 'last_login': None, 'date_join': '2018-11-01 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 1}}, {'user': {'username': 'usera', 'nickname': 'User A', 'email': 'usera@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-02-02 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 2}}, {'user': {'username': 'userb', 'nickname': 'User B', 'email': 'userb@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-03-03 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 3}}]} + {'code': 200, 'msg': 'success', '[]': [{'user': {'username': 'admin', 'nickname': 'Administrator', 'email': 'admin@localhost', 'is_superuser': True, 'last_login': None, 'date_join': '2018-01-01 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 1}}, {'user': {'username': 'usera', 'nickname': 'User A', 'email': 'usera@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-02-02 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 2}}, {'user': {'username': 'userb', 'nickname': 'User B', 'email': 'userb@localhost', 'is_superuser': False, 'last_login': None, 'date_join': '2018-03-03 00:00:00', 'image': '', 'active': False, 'locked': False, 'deleted': False, 'auth_type': 'default', 'timezone': '', 'id': 3}}]} >>> #query array ,@count is bad param >>> data ='''{ @@ -567,6 +567,200 @@ def test_apijson_get(): >>> print(d) {'code': 200, 'msg': 'success', '[]': [{'user': {'username': 'userc', 'nickname': 'User C', 'email': 'userc@localhost', 'id': 4}}, {'user': {'username': 'userb', 'nickname': 'User B', 'email': 'userb@localhost', 'id': 3}}]} + >>> #query array with @expr, bad param which is not list + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "@expr":{}, + ... "username$":"%b%", + ... "nickname$":"%c%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "only accept array in @expr, but get 'OrderedDict()'"} + + >>> #query array with @expr, bad param which is an empty list + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "@expr":[], + ... "username$":"%b%", + ... "nickname$":"%c%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "only accept 2 or 3 items in @expr, but get '[]'"} + + >>> #query array with @expr, bad param which is >3 items list + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "@expr":["username$","|","username$","|","nickname$"], + ... "username$":"%b%", + ... "nickname$":"%c%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "only accept 2 or 3 items in @expr, but get '['username$', '|', 'username$', '|', 'nickname$']'"} + + >>> #query array with @expr, bad param which have bad operator + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "@expr":["username$","*","nickname$"], + ... "username$":"%b%", + ... "nickname$":"%c%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "unknown operator: '*'"} + + >>> #query array with @expr, bad expr: & only 1 parameter + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "@expr":["&","nickname$"], + ... "username$":"%b%", + ... "nickname$":"%c%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'&'(and) expression need 3 items, but get '['&', 'nickname$']'"} + + >>> #query array with @expr, bad expr: | only 1 parameter + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "@expr":["|","nickname$"], + ... "username$":"%b%", + ... "nickname$":"%c%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'|'(or) expression need 3 items, but get '['|', 'nickname$']'"} + + >>> #query array with @expr, bad expr: | only 1 parameter + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "@expr":["username$","!","nickname$"], + ... "username$":"%b%", + ... "nickname$":"%c%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'!'(not) expression need 2 items, but get '['username$', '!', 'nickname$']'"} + + >>> #query array with like + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "username$":"%b%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', '[]': [{'user': {'username': 'userb', 'nickname': 'User B', 'email': 'userb@localhost', 'id': 3}}]} + + >>> #query array with like, but gave a nonexist column + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "nonexist$":"%b%" + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "model does not have this column: 'nonexist'"} + + >>> #query array with a nonexist column + >>> data ='''{ + ... "[]":{ + ... "@count":4, + ... "@page":0, + ... "user":{ + ... "@column":"id,username,nickname,email", + ... "@order":"id-", + ... "@role":"ADMIN", + ... "nonexist":1 + ... } + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "non-existent column or not support item: 'nonexist'"} + >>> #Association query: Two tables, one to one,ref path is absolute path >>> data ='''{ ... "moment":{}, @@ -580,6 +774,32 @@ def test_apijson_get(): >>> print(d) {'code': 200, 'msg': 'success', 'moment': {'user_id': 2, 'date': '2018-11-01 00:00:00', 'content': 'test moment', 'picture_list': '[]', 'id': 1}, 'user': {'username': 'usera', 'email': 'usera@localhost', 'id': 2}} + >>> #Association query: Two tables, one is array, one is single, there is a abs reference to array + >>> data ='''{ + ... "moment[]":{"moment":{"@count":3}}, + ... "user":{ + ... "@column": "id,username,email", + ... "id@": "moment[]/1/moment/user_id" + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment[]': [{'moment': {'user_id': 2, 'date': '2018-11-01 00:00:00', 'content': 'test moment', 'picture_list': '[]', 'id': 1}}, {'moment': {'user_id': 3, 'date': '2018-11-02 00:00:00', 'content': 'test moment from b', 'picture_list': '[]', 'id': 2}}, {'moment': {'user_id': 4, 'date': '2018-11-06 00:00:00', 'content': 'test moment from c', 'picture_list': '[]', 'id': 3}}], 'user': {'username': 'userb', 'email': 'userb@localhost', 'id': 3}} + + >>> #Association query: Two tables, one is array, one is single, there is a rel reference to array + >>> data ='''{ + ... "moment[]":{"moment":{"@count":3}}, + ... "user":{ + ... "@column": "id,username,email", + ... "id@": "/moment[]/1/moment/user_id" + ... } + ... }''' + >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment[]': [{'moment': {'user_id': 2, 'date': '2018-11-01 00:00:00', 'content': 'test moment', 'picture_list': '[]', 'id': 1}}, {'moment': {'user_id': 3, 'date': '2018-11-02 00:00:00', 'content': 'test moment from b', 'picture_list': '[]', 'id': 2}}, {'moment': {'user_id': 4, 'date': '2018-11-06 00:00:00', 'content': 'test moment from c', 'picture_list': '[]', 'id': 3}}], 'user': {'username': 'userb', 'email': 'userb@localhost', 'id': 3}} + >>> #Association query: Two tables, one to one,ref path is relative path >>> data ='''{ ... "moment":{}, @@ -593,3 +813,215 @@ def test_apijson_get(): >>> print(d) {'code': 200, 'msg': 'success', 'moment': {'user_id': 2, 'date': '2018-11-01 00:00:00', 'content': 'test moment', 'picture_list': '[]', 'id': 1}, 'user': {'username': 'usera', 'email': 'usera@localhost', 'id': 2}} """ + +def test_apijson_head(): + """ + >>> application = make_simple_application(project_dir='.') + >>> handler = application.handler() + + >>> #apijson head + >>> data ='''{ + ... "moment": { + ... "user_id": 2 + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment': {'code': 200, 'msg': 'success', 'count': 1}} + + >>> #apijson head, with a nonexistant model + >>> data ='''{ + ... "nonexist": { + ... "user_id": 2 + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "model 'nonexist' not found"} + + >>> #apijson head, without permission of HEAD + >>> data ='''{ + ... "privacy": { + ... "@role":"LOGIN", + ... "id": 1 + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "role 'LOGIN' not have permission HEAD for 'privacy'"} + + >>> #apijson head, without user + >>> data ='''{ + ... "privacy": { + ... "@role":"ADMIN", + ... "id": 1 + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "no login user for role 'ADMIN'"} + + >>> #apijson head, without user and @role + >>> data ='''{ + ... "privacy": { + ... "id": 1 + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "role 'UNKNOWN' not have permission HEAD for 'privacy'"} + + >>> #apijson head, user don't have role + >>> data ='''{ + ... "privacy": { + ... "@role":"ADMIN", + ... "id": 1 + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "user doesn't have role 'ADMIN'"} + + >>> #apijson head, with OWNER + >>> data ='''{ + ... "moment": { + ... "@role":"OWNER" + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + Moment: owner_condition + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment': {'code': 200, 'msg': 'success', 'count': 1}} + + >>> #apijson head, with OWNER but cannot filter with OWNER + >>> data ='''{ + ... "publicnotice": { + ... "@role":"OWNER" + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("usera"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'publicnotice' cannot filter with owner"} + + >>> #apijson head, with a nonexistant column + >>> data ='''{ + ... "moment": { + ... "nonexist": 2 + ... } + ... }''' + >>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'moment' don't have field 'nonexist'"} + """ + +def test_apijson_post(): + """ + >>> application = make_simple_application(project_dir='.') + >>> handler = application.handler() + + >>> #apijson post, without @tag + >>> data ='''{ + ... "moment": { + ... "content": "new moment for test", + ... "picture_list": ["http://static.oschina.net/uploads/user/48/96331_50.jpg"] + ... } + ... }''' + >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "'tag' parameter is needed"} + + >>> #apijson post + >>> data ='''{ + ... "moment": { + ... "content": "new moment for test", + ... "picture_list": ["http://static.oschina.net/uploads/user/48/96331_50.jpg"] + ... }, + ... "@tag": "moment" + ... }''' + >>> 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'}} + + >>> #apijson post to a non exist model + >>> data ='''{ + ... "nonexist": { + ... "content": "new moment for test", + ... "picture_list": ["http://static.oschina.net/uploads/user/48/96331_50.jpg"] + ... }, + ... "@tag": "nonexist" + ... }''' + >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "model 'nonexist' not found"} + + >>> #apijson post, tag is model which not define in APIJSON_REQUESTS + >>> data ='''{ + ... "moment": { + ... "content": "new moment for test", + ... "picture_list": ["http://static.oschina.net/uploads/user/48/96331_50.jpg"] + ... }, + ... "@tag": "role" + ... }''' + >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "tag 'role' not found"} + + >>> #apijson post, tag is model which not define in APIJSON_REQUESTS + >>> data ='''{ + ... "user": { + ... "username": "test" + ... }, + ... "@tag": "user" + ... }''' + >>> r = handler.post('/apijson/post', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 400, 'msg': "tag 'user' not found"} + """ + +def test_apijson_put(): + """ + >>> application = make_simple_application(project_dir='.') + >>> handler = application.handler() + + >>> #apijson put + >>> data ='''{ + ... "moment": { + ... "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': 200, 'msg': 'success', 'moment': {'id': 1, 'code': 200, 'msg': 'success', 'count': 1}} + + >>> #apijson put, with @role + >>> data ='''{ + ... "moment": { + ... "@role": "ADMIN", + ... "id": 1, + ... "content": "moment content after change 2" + ... }, + ... "@tag": "moment" + ... }''' + >>> r = handler.post('/apijson/put', data=data, pre_call=pre_call_as("admin"), middlewares=[]) + >>> d = json_loads(r.data) + >>> print(d) + {'code': 200, 'msg': 'success', 'moment': {'id': 1, 'code': 200, 'msg': 'success', 'count': 1}} + """ diff --git a/uliweb_apijson/apijson/README.md b/uliweb_apijson/apijson/README.md index 1a087bf..88c3bd3 100644 --- a/uliweb_apijson/apijson/README.md +++ b/uliweb_apijson/apijson/README.md @@ -4,10 +4,11 @@ uliweb-apijson is a subset and slightly different variation of [apijson](https:/ # Difference with original apijson -| feature | apijson(java) | uliweb-apijson | comment | -| -------- | ------------- | -------------- | ------------------------------------------------------------ | -| @combine | ✔️ | ✖️ | Example: "@combine":"&key0,&key1,\|key2,key3" | -| @expr | ✖️ | ✔️ | Example: "@expr":[["username$","&","email$"],"&",["!","nickname$"]] | +| feature | apijson(java) | uliweb-apijson | comment | +| ------------------- | --------------- | ---------------- | ------------------------------------------------------------ | +| @combine | ✔️ | ✖️ | Example: "@combine":"&key0,&key1,\|key2,key3" | +| @expr | ✖️ | ✔️ | Example: "@expr":[["username$","&","email$"],"&",["!","nickname$"]] | +| tag in apijson post/put | "tag": "Moment" | "@tag": "Moment" | | diff --git a/uliweb_apijson/apijson/__init__.py b/uliweb_apijson/apijson/__init__.py index 337f114..f57a0e7 100644 --- a/uliweb_apijson/apijson/__init__.py +++ b/uliweb_apijson/apijson/__init__.py @@ -163,7 +163,7 @@ def _get_array_q(self,params): #@expr model_expr = params.get("@expr") - if model_expr: + if model_expr!=None: c = self.parent._expr(self.model,params,model_expr) q = q.filter(c) else: @@ -258,5 +258,4 @@ def associated_query_array(self): del params[i] params.update(refs) q = self._get_array_q(params) - q = q.limit(1) item[self.name] = self._get_info(q.one()) diff --git a/uliweb_apijson/apijson/templates/vue/inc_apijson_table.html b/uliweb_apijson/apijson/templates/vue/inc_apijson_table.html index f9dea3b..367ff0f 100644 --- a/uliweb_apijson/apijson/templates/vue/inc_apijson_table.html +++ b/uliweb_apijson/apijson/templates/vue/inc_apijson_table.html @@ -53,6 +53,7 @@ var thisp = this return { l_request_tag: null, + role: "{{=role or ''}}", loading: false, modal_view: false, @@ -176,10 +177,13 @@ "@page":thisp.current_page-1, "@query":2 } - arr_params[thisp.model_name] = { - "@order":thisp.sort_key+thisp.sort_order, - "@role":"{{=role}}" + var model_params = { + "@order":thisp.sort_key+thisp.sort_order } + if (thisp.role!="") { + model_params["@role"] = thisp.role + } + arr_params[thisp.model_name] = model_params var params = { "[]":arr_params, "total@":"/[]/total" @@ -253,7 +257,7 @@ } } else { - this.viewedit_items.push({title:"id",value:row.id,component:"input"}) + this.viewedit_items.push({key:"id",title:"id",value:row.id,component:"input"}) var type2comp = { "boolean":"checkbox" } @@ -261,7 +265,7 @@ if (k!="id" && k[0]!="_") { var value = row[k] var comp = type2comp[typeof value] || "input" - this.viewedit_items.push({title:k,value:value,component:comp}) + this.viewedit_items.push({title:k,key:k,value:value,component:comp}) } } } @@ -297,6 +301,9 @@ record_params[d.key] = d.value } } + if (thisp.role!="") { + record_params["@role"] = thisp.role + } params[thisp.l_request_tag] = record_params params = thisp.ajax_hook("apijson_put","update",params) $.ajax({ @@ -377,9 +384,13 @@ var params = { "@tag": thisp.l_request_tag } - params[thisp.l_request_tag] = { + var model_params = { "id": thisp.delete_params.row.id } + if (thisp.role!="") { + model_params["@role"] = thisp.role + } + params[thisp.l_request_tag] = model_params params = thisp.ajax_hook("apijson_delete","delete",params) $.ajax({ type: "POST", @@ -433,6 +444,9 @@ var d = thisp.add_items[k] post_params[d.key] = d.value } + if (thisp.role!="") { + post_params["@role"] = thisp.role + } params[this.l_request_tag] = post_params params = thisp.ajax_hook("apijson_post","add",params) $.ajax({ diff --git a/uliweb_apijson/apijson/templates/vue/inc_apijson_viewedit.html b/uliweb_apijson/apijson/templates/vue/inc_apijson_viewedit.html index 0070b17..151a132 100644 --- a/uliweb_apijson/apijson/templates/vue/inc_apijson_viewedit.html +++ b/uliweb_apijson/apijson/templates/vue/inc_apijson_viewedit.html @@ -25,7 +25,7 @@ data: function(){ return { l_request_tag: null, - role: "{{=role}}", + role: "{{=role or ''}}", row: {}, row_saved: {}, viewedit_items: [], @@ -38,8 +38,10 @@ init_viewedit: function(){ var params = {} var model_params = { - "id":this.id, - "@role":this.role + "id":this.id + } + if (this.role!='') { + model_params["@role"] = this.role } params[this.model_name] = model_params var thisp = this @@ -89,6 +91,9 @@ this.row[d.key] = d.value } } + if (this.role!='') { + record_params["@role"] = this.role + } params[this.l_request_tag] = record_params params = this.ajax_hook("apijson_put","update",params) var thisp = this diff --git a/uliweb_apijson/apijson/views.py b/uliweb_apijson/apijson/views.py index 12128ec..4542988 100644 --- a/uliweb_apijson/apijson/views.py +++ b/uliweb_apijson/apijson/views.py @@ -112,12 +112,12 @@ def _get_one(self,key): roles = GET.get("roles") permission_check_ok = False if not params_role: - if hasattr(request,"user"): + if hasattr(request,"user") and request.user: params_role = "LOGIN" else: params_role = "UNKNOWN" elif params_role != "UNKNOWN": - if not hasattr(request,"user"): + if not (hasattr(request,"user") and request.user): return json({"code":400,"msg":"no login user for role '%s'"%(params_role)}) if params_role not in roles: return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(model_name,params_role)}) @@ -200,57 +200,61 @@ def _filter_owner(self,model,model_setting,q): def _expr(self,model,model_param,model_expr): if not isinstance(model_expr,list): - raise UliwebError("only accept array in @expr: '%s'"%(model_expr)) + raise UliwebError("only accept array in @expr, but get '%s'"%(model_expr)) num = len(model_expr) if (num<2 or num>3): - raise UliwebError("only accept 2 or 3 items in @expr: '%s'"%(model_expr)) + raise UliwebError("only accept 2 or 3 items in @expr, but get '%s'"%(model_expr)) op = model_expr[-2] if op=='&': if num!=3: - raise UliwebError("'&'(and) expression need 3 items: '%s'"%(model_expr)) + raise UliwebError("'&'(and) expression need 3 items, but get '%s'"%(model_expr)) c1 = self._get_filter_condition(model,model_param,model_expr[0],expr=True) c2 = self._get_filter_condition(model,model_param,model_expr[2],expr=True) return and_(c1,c2) elif op=='|': if num!=3: - raise UliwebError("'|'(or) expression need 3 items: '%s'"%(model_expr)) + raise UliwebError("'|'(or) expression need 3 items, but get '%s'"%(model_expr)) c1 = self._get_filter_condition(model,model_param,model_expr[0],expr=True) c2 = self._get_filter_condition(model,model_param,model_expr[2],expr=True) return or_(c1,c2) elif op=='!': if num!=2: - raise UliwebError("'!'(not) expression need 2 items: '%s'"%(model_expr)) + raise UliwebError("'!'(not) expression need 2 items, but get '%s'"%(model_expr)) return not_(self._get_filter_condition(model,model_param,model_expr[1],expr=True)) else: raise UliwebError("unknown operator: '%s'"%(op)) def _get_filter_condition(self,model,model_param,item,expr=False): + #item can be param key, or expr which expected to be a list if isinstance(item,list): if expr: return self._expr(model,model_param,model_expr=item) else: - raise UliwebError("item can be array only in @expr: '%s'"%(item)) + #current implementation won't run here, but keep for safe + raise UliwebError("item can be list only in @expr: '%s'"%(item)) if not isinstance(item,string_types): + #current implementation won't run here, but keep for safe raise UliwebError("item should be array or string: '%s'"%(item)) n = item if n[0]=="@": + #current implementation won't run here, but keep for safe raise UliwebError("param key should not begin with @: '%s'"%(n)) if n[-1]=="$": name = n[:-1] if hasattr(model,name): return getattr(model.c,name).like(model_param[n]) else: - raise UliwebError("'%s' does not have '%s'"%(model_name,name)) + raise UliwebError("model does not have this column: '%s'"%(name)) elif n[-1]=="}" and n[-2]=="{": name = n[:-2] if hasattr(model,name): - # TODO + # TODO: https://github.com/APIJSON/APIJSON/blob/master/Document.md#32-%E5%8A%9F%E8%83%BD%E7%AC%A6 pass raise UliwebError("still not support '%s'"%(name)) elif hasattr(model,n): return getattr(model.c,n)==model_param[n] else: - raise UliwebError("not support item: '%s'"%(item)) + raise UliwebError("non-existent column or not support item: '%s'"%(item)) def head(self): try: @@ -286,18 +290,21 @@ def _head(self,key): roles = HEAD.get("roles") permission_check_ok = False if not params_role: - if request.user: + if hasattr(request,"user") and request.user: params_role = "LOGIN" else: params_role = "UNKNOWN" if params_role not in roles: - return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(model_name,params_role)}) + return json({"code":400,"msg":"role '%s' not have permission HEAD for '%s'"%(params_role,model_name)}) if params_role == "UNKNOWN": permission_check_ok = True + elif not (hasattr(request,"user") and request.user): + return json({"code":400,"msg":"no login user for role '%s'"%(params_role)}) elif functions.has_role(request.user,params_role): permission_check_ok = True else: return json({"code":400,"msg":"user doesn't have role '%s'"%(params_role)}) + #current implementation won't run here, but keep for safe if not permission_check_ok: return json({"code":400,"msg":"no permission"}) @@ -381,7 +388,7 @@ def _post_one(self,key,tag): if roles: for role in roles: if role == "OWNER": - if request.user: + if hasattr(request,"user") and request.user: permission_check_ok = True if user_id_field: params[user_id_field] = request.user.id @@ -500,7 +507,7 @@ def _put_one(self,key,tag): if roles: for role in roles: if role == "OWNER": - if request.user: + if hasattr(request,"user") and request.user: if user_id_field: if obj.to_dict().get(user_id_field)==request.user.id: permission_check_ok = True @@ -535,6 +542,8 @@ def _put_one(self,key,tag): for k in params: if k=="id": continue + elif k[0]=="@": + continue elif hasattr(obj,k): kwargs[k] = params[k] else: @@ -621,7 +630,7 @@ def _delete_one(self,key,tag): if roles: for role in roles: if role == "OWNER": - if request.user: + if hasattr(request,"user") and request.user: if user_id_field: if obj.to_dict().get(user_id_field)==request.user.id: permission_check_ok = True