Browse Source

Allow queries by model's field

* All requests now can use any fields in the model except for binary and datetime
* Fields now support wildcard (*) to output all model fields
* GET request will return ID field if no fields are given
* Return proper HTTP error codes and messages in JSON format by default
pull/345/head
drpsyko101 9 months ago
parent
commit
4454c1be9d
  1. 244
      rest_api_odoo/controllers/main.py
  2. 51
      rest_api_odoo/static/description/index.html

244
rest_api_odoo/controllers/main.py

@ -23,7 +23,8 @@
import json
import logging
from odoo import http
from odoo.http import request
from odoo.http import request, Response
from ast import literal_eval
_logger = logging.getLogger(__name__)
@ -38,80 +39,83 @@ class RestApi(http.Controller):
user_id = request.env['res.users'].search([('api_key', '=', api_key)])
if api_key is not None and user_id:
response = True
return True
elif not user_id:
response = ('<html><body><h2>Invalid <i>API Key</i> '
'!</h2></body></html>')
else:
response = ("<html><body><h2>No <i>API Key</i> Provided "
"!</h2></body></html>")
return response
return Response(json.dumps({'message': 'Invalid API Key'}), status=401)
return Response(json.dumps({'message': 'No API Key Provided'}), status=400)
def simplest_type(self, input):
"""Try cast input into native Python class, otherwise return as string"""
try:
return literal_eval(input)
except:
if input == 'true':
return True
if input == 'false':
return False
return input
def generate_response(self, method, model, rec_id):
def generate_response(self, method, **query):
"""This function is used to generate the response based on the type
of request and the parameters given"""
model = query.pop('model')
option = request.env['connection.api'].search(
[('model_id', '=', model)], limit=1)
model_name = option.model_id.model
model_display_name = option.model_id.name
if method != 'DELETE':
data = {}
try:
data = json.loads(request.httprequest.data)
else:
data = {}
except:
pass
fields = []
if data:
for field in data['fields']:
fields.append(field)
if not fields and method != 'DELETE':
return ("<html><body><h2>No fields selected for the model"
"</h2></body></html>")
if '*' in fields:
fields = []
record_fields = request.env[
str(model_name)].fields_get([], attributes=['type'])
for field, value in record_fields.items():
value_type = value.get('type')
if not (value_type == 'binary' or value_type == 'datetime'):
fields.append(field)
if not option:
return ("<html><body><h2>No Record Created for the model"
"</h2></body></html>")
return Response(json.dumps({'message': f'No Record Created for the model. Please contact your admininstrator to enable {method} method for {model_display_name} record.'}), status=403)
try:
if method == 'GET':
fields = []
for field in data['fields']:
fields.append(field)
if not fields:
fields.append('id')
if not option.is_get:
return ("<html><body><h2>Method Not Allowed"
"</h2></body></html>")
return Response(
json.dumps({'message': f'Method not allowed. Please contact your admininstrator to enable {method} method for {model_display_name} record.'}),
status=405)
else:
datas = []
if rec_id != 0:
partner_records = request.env[
str(model_name)].search_read(
domain=[('id', '=', rec_id)],
fields=fields
)
data = json.dumps({
'records': partner_records
})
datas.append(data)
return request.make_response(data=datas)
else:
partner_records = request.env[
str(model_name)].search_read(
domain=[],
fields=fields
)
data = json.dumps({
domains = []
for key, value in query.items():
domains.append((key, '=', self.simplest_type(value)))
partner_records = request.env[
str(model_name)].search_read(
domain=domains,
fields=fields
)
return Response(
json.dumps({
'records': partner_records
})
datas.append(data)
return request.make_response(data=datas)
except:
return ("<html><body><h2>Invalid JSON Data"
"</h2></body></html>")
if method == 'POST':
if not option.is_post:
return ("<html><body><h2>Method Not Allowed"
"</h2></body></html>")
else:
)
if method == 'POST':
if not option.is_post:
return Response(
json.dumps({'message': f'Method not allowed. Please contact your admininstrator to enable {method} method for {model_display_name} record.'}),
status=405)
if not data or 'values' not in data:
return Response(json.dumps({'message': 'No Data Provided'}), status=403)
try:
data = json.loads(request.httprequest.data)
datas = []
new_resource = request.env[str(model_name)].create(
data['values'])
partner_records = request.env[
@ -119,71 +123,61 @@ class RestApi(http.Controller):
domain=[('id', '=', new_resource.id)],
fields=fields
)
new_data = json.dumps({'New resource': partner_records, })
datas.append(new_data)
return request.make_response(data=datas)
return Response(json.dumps({'new_record': partner_records}), status=201)
except:
return ("<html><body><h2>Invalid JSON Data"
"</h2></body></html>")
if method == 'PUT':
if not option.is_put:
return ("<html><body><h2>Method Not Allowed"
"</h2></body></html>")
else:
if rec_id == 0:
return ("<html><body><h2>No ID Provided"
"</h2></body></html>")
else:
resource = request.env[str(model_name)].browse(
int(rec_id))
if not resource.exists():
return ("<html><body><h2>Resource not found"
"</h2></body></html>")
else:
try:
datas = []
data = json.loads(request.httprequest.data)
resource.write(data['values'])
partner_records = request.env[
str(model_name)].search_read(
domain=[('id', '=', resource.id)],
fields=fields
)
new_data = json.dumps(
{'Updated resource': partner_records,
})
datas.append(new_data)
return request.make_response(data=datas)
except:
return ("<html><body><h2>Invalid JSON Data "
"!</h2></body></html>")
if method == 'DELETE':
if not option.is_delete:
return ("<html><body><h2>Method Not Allowed"
"</h2></body></html>")
else:
if rec_id == 0:
return ("<html><body><h2>No ID Provided"
"</h2></body></html>")
return Response(json.dumps({'message': 'Invalid JSON Data'}), status=403)
if method == 'PUT':
if not option.is_put:
return Response(
json.dumps({'message': f'Method not allowed. Please contact your admininstrator to enable {method} method for {model_display_name} record.'}),
status=405)
if not 'id' in query:
return Response(json.dumps({'message': 'No ID Provided'}), status=403)
if not data or 'values' not in data:
return Response(json.dumps({'message': 'No Data Provided'}), status=403)
resource = request.env[str(model_name)].browse(
int(query.get('id')))
if not resource.exists():
return Response(json.dumps({'message': 'Resource not found'}), status=404)
try:
data = json.loads(request.httprequest.data)
resource.write(data['values'])
partner_records = request.env[
str(model_name)].search_read(
domain=[('id', '=', resource.id)],
fields=fields
)
return Response(json.dumps({'updated_record': partner_records}))
except:
return Response(json.dumps({'message': 'Invalid JSON value(s) passed'}), status=403)
if method == 'DELETE':
if not option.is_delete:
return Response(
json.dumps({'message': f'Method not allowed. Please contact your admininstrator to enable {method} method for {model_display_name} record.'}),
status=405)
if not 'id' in query:
return Response(json.dumps({'message': 'No ID Provided'}), status=403)
resource = request.env[str(model_name)].browse(
int(query.get('id')))
if not resource.exists():
return Response(json.dumps({'message': 'Resource not found'}), status=404)
else:
resource = request.env[str(model_name)].browse(
int(rec_id))
if not resource.exists():
return ("<html><body><h2>Resource not found"
"</h2></body></html>")
else:
records = request.env[
str(model_name)].search_read(
domain=[('id', '=', resource.id)],
fields=['id', 'display_name']
)
remove = json.dumps(
{"Resource deleted": records,
})
resource.unlink()
return request.make_response(data=remove)
records = request.env[
str(model_name)].search_read(
domain=[('id', '=', resource.id)],
fields=fields
)
resource.unlink()
return Response(json.dumps({'message': 'Resource deleted', 'data': records}), status=202)
except:
return Response(json.dumps({'message': 'Internal Server Error'}), status=500)
@http.route(['/send_request'], type='http',
auth='none',
@ -196,7 +190,7 @@ class RestApi(http.Controller):
http_method = request.httprequest.method
api_key = request.httprequest.headers.get('api-key')
auth_api = self.auth_api_key(api_key)
model = kw.get('model')
model = kw.pop('model')
username = request.httprequest.headers.get('login')
password = request.httprequest.headers.get('password')
request.session.authenticate(request.session.db, username,
@ -204,17 +198,12 @@ class RestApi(http.Controller):
model_id = request.env['ir.model'].search(
[('model', '=', model)])
if not model_id:
return ("<html><body><h3>Invalid model, check spelling or maybe "
"the related "
"module is not installed"
"</h3></body></html>")
return Response(json.dumps(
{'message': 'Invalid model, check spelling or maybe the related module is not installed'}),
status=403)
if auth_api == True:
if not kw.get('Id'):
rec_id = 0
else:
rec_id = int(kw.get('Id'))
result = self.generate_response(http_method, model_id.id, rec_id)
result = self.generate_response(http_method, model=model_id.id, **kw)
return result
else:
return auth_api
@ -239,5 +228,4 @@ class RestApi(http.Controller):
"api-key": api_key})
return request.make_response(data=datas)
except:
return ("<html><body><h2>wrong login credentials"
"</h2></body></html>")
return Response(json.dumps({'message': 'wrong login credentials'}), status=401)

51
rest_api_odoo/static/description/index.html

@ -217,7 +217,7 @@
<li>First of all, we have to add a new parameter in odoo conf.
file.
</li>
<li><b>server_wide_modules = web, base, rest_api_odoo</b><br/>
<li><code>server_wide_modules = web, base, rest_api_odoo</code><br/>
- This will allow us to send request to server without
selecting database first.<br/>- Incase if you have to
uninstall the module , you have to remove this parameter.
@ -251,10 +251,10 @@
<li>Next you have to select the database and login.</li>
<li>We have attached <b>Postman collections</b> through which
you can
authenticate rest api.
authenticate REST api.
</li>
<li>First, extract the <b>zip</b> file. Then, you will obtain the JSON-format file, which you can directly import into <b>POSTMAN.</b></li>
<li>The url format will be like this - <b>http://cybrosys:8016/odoo_connect</b>
<li>The url format will be like this - <code>http://cybrosys:8016/odoo_connect</code>
Replace 'cybrosys:8016' with your localhost port number.
</li>
<li>You have to provide database name, username and password
@ -266,9 +266,9 @@
<li>This key will be used when sending api requests to
database.
</li>
<li>The response will be like this - <b> {"Status": "auth
<li>The response will be like this - <code>{"Status": "auth
successful", "User": "Mitchell Admin", "api-key":
"66c2ebab-d4dc-42f0-87d0-d1646e887569"}.</b></li>
"66c2ebab-d4dc-42f0-87d0-d1646e887569"}</code>.</li>
</ul>
</p>
<img src="assets/screenshots/screenshot_2.png"
@ -307,7 +307,7 @@
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
<ul>
<li>You can send GET request to retrieve data from the
<li>You can send <code>GET</code> request to retrieve data from the
database.
</li>
<li>The postman collection has been provided with app files for
@ -318,15 +318,16 @@
</li>
<li>Model can be passed as argument as the technical name , and
also if you want
specific record you can provide the id as well.
specific record you can provide any related fields to the module as well.
</li>
<li>The format for GET method will be like this - <b>http://cybrosys:8016/send_request?model=res.partner&Id=10.</b>
<li>The format for GET method will be like this - <code>http://cybrosys:8016/send_request?model=res.partner&Id=10.</code>
</li>
<li>We can specify the fields inside the JSON data, and it will
be like this - <b>{"fields": ["name", "email"]}.</b></li>
<li>This is the format of api response - <b>{"records": [{"id":
be like this - <code>{"fields": ["name", "email"]}</code>.</li>
<li>This is the format of api response - <code>{"records": [{"id":
10, "email": "deco.addict82@example.com", "name": "Deco
Addict"}]}.</b>
Addict"}]}</code>. If no fields are passed , it will returns
just the record's ID. To get all of the fields, set the fields to wildcard - <code>{"fields": ["*"]}</code>.
</li>
</ul>
@ -338,7 +339,7 @@
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
<ul>
<li>Using POST method , you can create new records in the
<li>Using <code>POST</code> method , you can create new records in the
database.
</li>
<li>Just make sure you enabled POST method for the model record
@ -351,18 +352,18 @@
<li>You can make use of the postman collection that we have
added with app files.
</li>
<li>The format for sending POST request will be like this - <b>http://cybrosys:8016/send_request?model=res.partner.</b>
<li>The format for sending POST request will be like this - <code>http://cybrosys:8016/send_request?model=res.partner.</code>
</li>
<li>This is the format for JSON data - <b>{
<li>This is the format for JSON data - <code>{
"fields" :["name", "phone"] ,
"values": {"name": "abc",
"phone":"55962441552"
} }.</b>
} }.</code>
</li>
<li>Make sure the data entered in correct format otherwise you
will get <b>'Invalid JSON data' message.</b></li>
<li>Response will be in this format - <b>{"New resource":
[{"id": 51, "name": "abc", "phone": "55962441552"}]}.</b>
<li>Response will be in this format - <code>{"New resource":
[{"id": 51, "name": "abc", "phone": "55962441552"}]}</code>.
</li>
</ul>
</p>
@ -373,7 +374,7 @@
Update Records
</h3>
<ul>
<li>Updation of records in the database can be done with PUT
<li>Updation of records in the database can be done with <code>PUT</code>
method.
</li>
<li>You have to provide the model and also the id or the record
@ -383,14 +384,14 @@
will be always have to send request with your login
credentials. Otherwise, it will be showing access denied.
</li>
<li>The format for sending PUT request will be like this - <b>http://cybrosys:8016/send_request?model=res.partner&Id=46.</b>
<li>The format for sending PUT request will be like this - <code>http://cybrosys:8016/send_request?model=res.partner&Id=46.</code>
</li>
<li>Here too you have to provide the JSON data through which the
updates will be done.
</li>
<li>The response format will be like this - <b>{"Updated
<li>The response format will be like this - <code>{"Updated
resource": [{"id": 46, "email": "abc@example.com", "name":
"Toni"}]}.</b></li>
"Toni"}]}</code>.</li>
</ul>
</p>
@ -401,7 +402,7 @@
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
<ul>
<li>Database records can be deleted by sending DELETE method
<li>Database records can be deleted by sending <code>DELETE</code> method
request.
</li>
<li>For the deletion we have to provide the Model and the record
@ -410,11 +411,11 @@
<li>Make sure you have permission to delete files for the
selected model in the rest api record.
</li>
<li>The delete request format will be like this - <b>http://cybrosys:8016/send_request?model=res.partner&Id=46.</b>
<li>The delete request format will be like this - <code>http://cybrosys:8016/send_request?model=res.partner&Id=46.</code>
</li>
<li> The response after successful deletion will be -<b>
<li> The response after successful deletion will be -<code>
{"Resource deleted": [{"id": 46, "email": "abc@example.com",
"name": "Toni"}]}.</b></li>
"name": "Toni"}]}</code>.</li>
</ul>
</p>

Loading…
Cancel
Save