Merge branch 'master-rest-api-issues' into 'master'
[FIX] Fixes REST API issues #70, #71 and #73 and add basic guideline of Rest API on description page See merge request flectra-hq/flectra!70
This commit is contained in:
commit
846520e5d6
@ -55,108 +55,100 @@ def eval_json_to_data(modelname, json_data, create=True):
|
||||
return values
|
||||
|
||||
|
||||
def object_read(modelname, default_domain, status_code,
|
||||
post={}):
|
||||
json_data = post
|
||||
domain = default_domain or []
|
||||
field = []
|
||||
def object_read(model_name, params, status_code):
|
||||
domain = []
|
||||
fields = []
|
||||
offset = 0
|
||||
limit = None
|
||||
order = None
|
||||
if 'filters' in json_data:
|
||||
domain += ast.literal_eval(json_data['filters'])
|
||||
elif 'field' in json_data:
|
||||
field += ast.literal_eval(json_data['field'])
|
||||
elif 'offset' in json_data:
|
||||
offset = int(json_data['offset'])
|
||||
elif 'limit' in json_data:
|
||||
limit = int(json_data['limit'])
|
||||
elif 'order' in json_data:
|
||||
order = json_data['order']
|
||||
else:
|
||||
pass
|
||||
# Search Read object:
|
||||
data = request.env[modelname].search_read(domain, offset=offset, limit=limit, order=order)
|
||||
return valid_response(status=status_code,
|
||||
data={
|
||||
'count': len(data), 'results': data
|
||||
})
|
||||
if 'filters' in params:
|
||||
domain += ast.literal_eval(params['filters'])
|
||||
if 'field' in params:
|
||||
fields += ast.literal_eval(params['field'])
|
||||
if 'offset' in params:
|
||||
offset = int(params['offset'])
|
||||
if 'limit' in params:
|
||||
limit = int(params['limit'])
|
||||
if 'order' in params:
|
||||
order = params['order']
|
||||
|
||||
|
||||
def object_read_one(modelname, id, status_code):
|
||||
try:
|
||||
id = int(id)
|
||||
except:
|
||||
pass
|
||||
|
||||
if not id:
|
||||
return invalid_object_id()
|
||||
data = request.env[modelname].search_read(domain=[('id', '=', id)])
|
||||
data = request.env[model_name].search_read(domain=domain, fields=fields, offset=offset, limit=limit, order=order)
|
||||
if data:
|
||||
return valid_response(status_code, data)
|
||||
return valid_response(status=status_code, data={
|
||||
'count': len(data),
|
||||
'results': data
|
||||
})
|
||||
else:
|
||||
return object_not_found()
|
||||
return object_not_found_all(model_name)
|
||||
|
||||
|
||||
def object_create_one(modelname, data, status_code):
|
||||
rdata = request.httprequest.stream.read().decode('utf-8')
|
||||
json_data = json.loads(rdata)
|
||||
vals = eval_json_to_data(modelname, json_data)
|
||||
if data:
|
||||
vals.update(data)
|
||||
def object_read_one(model_name, rec_id, params, status_code):
|
||||
fields = []
|
||||
if 'field' in params:
|
||||
fields += ast.literal_eval(params['field'])
|
||||
try:
|
||||
res = request.env[modelname].create(vals)
|
||||
flectra_error = ''
|
||||
rec_id = int(rec_id)
|
||||
except Exception as e:
|
||||
res = None
|
||||
flectra_error = e
|
||||
rec_id = False
|
||||
|
||||
if not rec_id:
|
||||
return invalid_object_id()
|
||||
data = request.env[model_name].search_read(domain=[('id', '=', rec_id)], fields=fields)
|
||||
if data:
|
||||
return valid_response(status=status_code, data=data)
|
||||
else:
|
||||
return object_not_found(rec_id, model_name)
|
||||
|
||||
|
||||
def object_create_one(model_name, data, status_code):
|
||||
try:
|
||||
res = request.env[model_name].create(data)
|
||||
except Exception as e:
|
||||
return no_object_created(e)
|
||||
if res:
|
||||
return valid_response(status_code, {'id': res.id})
|
||||
else:
|
||||
return no_object_created(flectra_error)
|
||||
|
||||
|
||||
def object_update_one(modelname, id, status_code):
|
||||
def object_update_one(model_name, rec_id, data, status_code):
|
||||
try:
|
||||
id = int(id)
|
||||
except:
|
||||
id = None
|
||||
if not id:
|
||||
return invalid_object_id()
|
||||
rdata = request.httprequest.stream.read().decode('utf-8')
|
||||
json_data = ast.literal_eval(rdata)
|
||||
vals = eval_json_to_data(modelname, json_data, create=False)
|
||||
try:
|
||||
res = request.env[modelname].browse(id).write(vals)
|
||||
flectra_error = ''
|
||||
rec_id = int(rec_id)
|
||||
except Exception as e:
|
||||
res = None
|
||||
flectra_error = e
|
||||
if res:
|
||||
return valid_response(status_code, 'Record Updated '
|
||||
'successfully!')
|
||||
else:
|
||||
return no_object_updated(flectra_error)
|
||||
rec_id = None
|
||||
|
||||
|
||||
def object_delete_one(modelname, id, status_code):
|
||||
try:
|
||||
id = int(id)
|
||||
except:
|
||||
id = None
|
||||
if not id:
|
||||
if not rec_id:
|
||||
return invalid_object_id()
|
||||
|
||||
try:
|
||||
res = request.env[modelname].browse(id).unlink()
|
||||
flectra_error = ''
|
||||
res = request.env[model_name].search([('id', '=', rec_id)])
|
||||
if res:
|
||||
res.write(data)
|
||||
else:
|
||||
return object_not_found(rec_id, model_name)
|
||||
except Exception as e:
|
||||
res = None
|
||||
flectra_error = e
|
||||
return no_object_updated(e)
|
||||
if res:
|
||||
return valid_response(status_code, 'Record Successfully '
|
||||
'Deleted!')
|
||||
else:
|
||||
return no_object_deleted(flectra_error)
|
||||
return valid_response(status_code, {'desc': 'Record Updated successfully!', 'update': True})
|
||||
|
||||
|
||||
def object_delete_one(model_name, rec_id, status_code):
|
||||
try:
|
||||
rec_id = int(rec_id)
|
||||
except Exception as e:
|
||||
rec_id = None
|
||||
|
||||
if not rec_id:
|
||||
return invalid_object_id()
|
||||
|
||||
try:
|
||||
res = request.env[model_name].search([('id', '=', rec_id)])
|
||||
if res:
|
||||
res.unlink()
|
||||
else:
|
||||
return object_not_found(rec_id, model_name)
|
||||
except Exception as e:
|
||||
return no_object_deleted(e)
|
||||
if res:
|
||||
return valid_response(status_code, {'desc': 'Record Successfully Deleted!', 'delete': True})
|
||||
|
||||
|
||||
def check_valid_token(func):
|
||||
@ -284,7 +276,7 @@ class ControllerREST(http.Controller):
|
||||
# Successful response:
|
||||
return valid_response(
|
||||
200,
|
||||
{}
|
||||
{"desc": 'Token Successfully Deleted', "delete": True}
|
||||
)
|
||||
|
||||
@http.route([
|
||||
@ -295,45 +287,27 @@ class ControllerREST(http.Controller):
|
||||
@check_valid_token
|
||||
def restapi_access_token(self, model_name=False, id=False, **post):
|
||||
Model = request.env['ir.model']
|
||||
Model_ids = Model.sudo().search([('model', '=', model_name),
|
||||
('rest_api', '=', True)])
|
||||
if Model_ids:
|
||||
return getattr(self, '%s_data' % (
|
||||
request.httprequest.method).lower())(
|
||||
model_name=model_name, id=id, **post)
|
||||
return object_not_found()
|
||||
Model_id = Model.sudo().search([('model', '=', model_name)], limit=1)
|
||||
|
||||
def get_data(self, model_name=False, id=False, **post):
|
||||
if Model_id:
|
||||
if Model_id.rest_api:
|
||||
return getattr(self, '%s_data' % (
|
||||
request.httprequest.method).lower())(
|
||||
model_name=model_name, id=id, **post)
|
||||
else:
|
||||
return rest_api_unavailable(model_name)
|
||||
return modal_not_found(model_name)
|
||||
|
||||
def get_data(self, model_name=False, id=False, **get):
|
||||
if id:
|
||||
return object_read_one(
|
||||
modelname=model_name,
|
||||
id=id,
|
||||
status_code=200,
|
||||
)
|
||||
return object_read(
|
||||
modelname=model_name,
|
||||
default_domain=[],
|
||||
status_code=200,
|
||||
post=post
|
||||
)
|
||||
return object_read_one(model_name, id, get, status_code=200)
|
||||
return object_read(model_name, get, status_code=200)
|
||||
|
||||
def put_data(self, model_name=False, id=False, **post):
|
||||
return object_update_one(
|
||||
modelname=model_name,
|
||||
id=id,
|
||||
status_code=200,
|
||||
)
|
||||
def put_data(self, model_name=False, id=False, **put):
|
||||
return object_update_one(model_name, id, put, status_code=200)
|
||||
|
||||
def post_data(self, model_name=False, id=False, **post):
|
||||
return object_create_one(
|
||||
modelname=model_name,
|
||||
data={},
|
||||
status_code=200,
|
||||
)
|
||||
def post_data(self, model_name=False, **post):
|
||||
return object_create_one(model_name, post, status_code=200)
|
||||
|
||||
def delete_data(self, model_name=False, id=False):
|
||||
return object_delete_one(
|
||||
modelname=model_name,
|
||||
id=id,
|
||||
status_code=200
|
||||
)
|
||||
return object_delete_one(model_name, id, status_code=200)
|
||||
|
@ -11,11 +11,18 @@ except ImportError:
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class JSONEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, (bytes, bytearray)):
|
||||
return obj.decode("utf-8")
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
|
||||
def valid_response(status, data):
|
||||
return werkzeug.wrappers.Response(
|
||||
status=status,
|
||||
content_type='application/json; charset=utf-8',
|
||||
response=json.dumps(data),
|
||||
response=json.dumps(data, cls=JSONEncoder),
|
||||
)
|
||||
|
||||
|
||||
@ -39,35 +46,49 @@ def invalid_token():
|
||||
_logger.error("Token is expired or invalid!")
|
||||
return invalid_response(401, 'invalid_token', "Token is expired or invalid!")
|
||||
|
||||
|
||||
def object_not_found():
|
||||
def modal_not_found(modal_name):
|
||||
_logger.error("Not found object(s) in flectra!")
|
||||
return invalid_response(404, 'not_found_object_in_flectra',
|
||||
"Not found object(s) in flectra!")
|
||||
return invalid_response(404, 'object_not_found_in_flectra',
|
||||
"Modal " + modal_name + " Not Found!")
|
||||
|
||||
def rest_api_unavailable(modal_name):
|
||||
_logger.error("Not found object(s) in flectra!")
|
||||
return invalid_response(404, 'object_not_found_in_flectra',
|
||||
"Enable Rest API For " + modal_name + "!")
|
||||
|
||||
def object_not_found_all(modal_name):
|
||||
_logger.error("Not found object(s) in flectra!")
|
||||
return invalid_response(404, 'object_not_found_in_flectra',
|
||||
"No Record found in " + modal_name + "!")
|
||||
|
||||
def object_not_found(record_id, modal_name):
|
||||
_logger.error("Not found object(s) in flectra!")
|
||||
return invalid_response(404, 'object_not_found_in_flectra',
|
||||
"Record " + str(record_id) + " Not found in " + modal_name + "!")
|
||||
|
||||
|
||||
def unable_delete():
|
||||
_logger.error("Access Denied!")
|
||||
return invalid_response(404, "you don't have access to delete records for "
|
||||
return invalid_response(403, "you don't have access to delete records for "
|
||||
"this model", "Access Denied!")
|
||||
|
||||
|
||||
def no_object_created(flectra_error):
|
||||
_logger.error("Not created object in flectra! ERROR: %s" % flectra_error)
|
||||
return invalid_response(409, 'not_created_object_in_flectra',
|
||||
return invalid_response(500, 'not_created_object_in_flectra',
|
||||
"Not created object in flectra! ERROR: %s" %
|
||||
flectra_error)
|
||||
|
||||
|
||||
def no_object_updated(flectra_error):
|
||||
_logger.error("Not updated object in flectra! ERROR: %s" % flectra_error)
|
||||
return invalid_response(409, 'not_updated_object_in_flectra',
|
||||
"Not updated object in flectra! ERROR: %s" %
|
||||
return invalid_response(500, 'not_updated_object_in_flectra',
|
||||
"Object Not Updated! ERROR: %s" %
|
||||
flectra_error)
|
||||
|
||||
|
||||
def no_object_deleted(flectra_error):
|
||||
_logger.error("Not deleted object in flectra! ERROR: %s" % flectra_error)
|
||||
return invalid_response(409, 'not_deleted_object_in_flectra',
|
||||
return invalid_response(500, 'not_deleted_object_in_flectra',
|
||||
"Not deleted object in flectra! ERROR: %s" %
|
||||
flectra_error)
|
||||
|
315
addons/rest_api/static/description/index.html
Normal file
315
addons/rest_api/static/description/index.html
Normal file
@ -0,0 +1,315 @@
|
||||
<section class="container">
|
||||
<div class="row oe_spaced">
|
||||
<h2 class="text-center">Guideline of Rest API</h2>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<div class="col-md-12 bg-black">
|
||||
<h3 class="text-center">Generate Token</h3>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
import requests
|
||||
|
||||
db_config = {
|
||||
"username': "YOUR_EMAIL",
|
||||
"password': "YOUR_PASSWORD",
|
||||
"db': "SERVER_DATABASE_NAME"
|
||||
}
|
||||
server_url = "YOUR_SERVER_URL"
|
||||
get_token_url = "/api/auth/get_tokens"
|
||||
url = server_url + get_token_url
|
||||
|
||||
requests.post(url=url, data=db_config)
|
||||
|
||||
</code>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
<u><b>output:</b></u>
|
||||
{
|
||||
"company_id": 1,
|
||||
"user_context": {
|
||||
"lang": "en_US",
|
||||
"tz": "Asia/Kolkata",
|
||||
"uid": 1
|
||||
},
|
||||
"access_token": "YOUR_API_ACCESS_TOKEN",
|
||||
"expires_in": "600",
|
||||
"uid": 1
|
||||
}
|
||||
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<div class="col-md-12 bg-black">
|
||||
<h3 class="text-center">Delete Token</h3>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
import requests
|
||||
|
||||
header = {
|
||||
"access_token": "YOUR_API_ACCESS_TOKEN"
|
||||
}
|
||||
server_url = "YOUR_SERVER_URL"
|
||||
del_token_url = "/api/auth/delete_tokens"
|
||||
url = server_url + del_token_url
|
||||
|
||||
requests.post(url=url, headers=header)
|
||||
|
||||
</code>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
<u><b>output:</b></u>
|
||||
{
|
||||
"desc": "Token Successfully Deleted",
|
||||
"delete": true
|
||||
}
|
||||
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<div class="col-md-12 bg-black">
|
||||
<h3 class="text-center">POST Request</h3>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
import requests
|
||||
|
||||
header = {
|
||||
"access_token": "YOUR_API_ACCESS_TOKEN"
|
||||
}
|
||||
data={
|
||||
"name": "FlectraHQ",
|
||||
"email": "example@flectrahq.com",
|
||||
"website": "www.flectrahq.com"
|
||||
}
|
||||
server_url = "YOUR_SERVER_URL"
|
||||
post_url = '/api/res.partner'
|
||||
url = server_url + post_url
|
||||
|
||||
requests.post(url=url, data=data, headers=header)
|
||||
|
||||
</code>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
<u><b>output:</b></u>
|
||||
{
|
||||
"id": 300
|
||||
}
|
||||
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<div class="col-md-12 bg-black">
|
||||
<h3 class="text-center">GET Request</h3>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
"""
|
||||
|
||||
"limit" : Integer eg. 10,
|
||||
"field" : [List of Field(s) in String]
|
||||
eg. "['name', 'email', ..]",
|
||||
"order" : "String" eg. "name asc/desc",
|
||||
"offset" : Integer eg. 3,
|
||||
"filters" : [List of Condition(s) in String]
|
||||
eg. "[('name', 'like', 'XXX'), ..]"
|
||||
|
||||
"""
|
||||
|
||||
import requests
|
||||
|
||||
header = {
|
||||
"access_token": "YOUR_API_ACCESS_TOKEN"
|
||||
}
|
||||
data={
|
||||
"limit": 3,
|
||||
"field": "['name', 'email']",
|
||||
"order": 'name desc'
|
||||
}
|
||||
server_url = "YOUR_SERVER_URL"
|
||||
get_url = '/api/res.partner'
|
||||
url = server_url + get_url
|
||||
|
||||
requests.get(url=url, data=data, headers=header)
|
||||
|
||||
|
||||
</code>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
<u><b>output:</b></u>
|
||||
{
|
||||
"count": 3,
|
||||
"results": [
|
||||
{
|
||||
"email": false,
|
||||
"id": 44,
|
||||
"name": "Your Company"
|
||||
},
|
||||
{
|
||||
"email": "info@yourcompany.example.com",
|
||||
"id": 1,
|
||||
"name": "YourCompany"
|
||||
},
|
||||
{
|
||||
"email": "william.jackson@jackson.example.com",
|
||||
"id": 33,
|
||||
"name": "William Thomas"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12 bg-black">
|
||||
<h5 class="text-center">GET Request for Single Record</h5>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
"""
|
||||
|
||||
"field" : [List of Field(s) in String]
|
||||
eg. "['name', 'email', ..]",
|
||||
|
||||
"""
|
||||
|
||||
import requests
|
||||
|
||||
header = {
|
||||
"access_token": "YOUR_API_ACCESS_TOKEN"
|
||||
}
|
||||
data={
|
||||
"field": "['name', 'email']",
|
||||
}
|
||||
server_url = "YOUR_SERVER_URL"
|
||||
get_url = '/api/res.partner/1'
|
||||
url = server_url + get_url
|
||||
|
||||
requests.get(url=url, data=data, headers=header)
|
||||
|
||||
|
||||
</code>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
<u><b>output:</b></u>
|
||||
[
|
||||
{
|
||||
"email": "info@yourcompany.example.com",
|
||||
"name": "YourCompany",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<div class="col-md-12 bg-black">
|
||||
<h3 class="text-center">PUT Request</h3>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
import requests
|
||||
|
||||
header = {
|
||||
"access_token": "YOUR_API_ACCESS_TOKEN"
|
||||
}
|
||||
data={
|
||||
"name": "FlectraHQ",
|
||||
"email": "example@flectrahq.com",
|
||||
"website": "www.flectrahq.com"
|
||||
"id": 300
|
||||
}
|
||||
server_url = "YOUR_SERVER_URL"
|
||||
put_url = '/api/res.partner'
|
||||
url = server_url + put_url
|
||||
|
||||
requests.put(url=url, data=data, headers=header)
|
||||
|
||||
</code>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
<u><b>output:</b></u>
|
||||
{
|
||||
"desc": "Record Updated successfully!",
|
||||
"update": true
|
||||
}
|
||||
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<div class="col-md-12 bg-black">
|
||||
<h3 class="text-center">DELETE Request</h3>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
import requests
|
||||
|
||||
header = {
|
||||
"access_token": "YOUR_API_ACCESS_TOKEN"
|
||||
}
|
||||
data={
|
||||
"id": 300
|
||||
}
|
||||
server_url = "YOUR_SERVER_URL"
|
||||
put_url = '/api/res.partner'
|
||||
url = server_url + put_url
|
||||
|
||||
requests.delete(url=url, data=data, headers=header)
|
||||
|
||||
</code>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<code class="bg-black" style="white-space: pre-wrap;">
|
||||
|
||||
<u><b>output:</b></u>
|
||||
{
|
||||
"desc": "Record Successfully Deleted!",
|
||||
"delete": true
|
||||
}
|
||||
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
Loading…
Reference in New Issue
Block a user