Skip to content

Instantly share code, notes, and snippets.

@tienhieuD
Created April 25, 2019 04:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tienhieuD/83da9ae75f11bd783b35a2967756ee1f to your computer and use it in GitHub Desktop.
Save tienhieuD/83da9ae75f11bd783b35a2967756ee1f to your computer and use it in GitHub Desktop.
"""Part of odoo. See LICENSE file for full copyright and licensing details."""
import simplejson as json
from simplejson.errors import JSONDecodeError
import werkzeug.wrappers
import functools
import logging
from odoo import http
from odoo.http import request
_logger = logging.getLogger(__name__)
def valid_response(data, status=200):
"""Valid Response
This will be return when the http request was successfully processed."""
data = {
'count': len(data),
'data': data
}
return werkzeug.wrappers.Response(
status=status,
content_type='application/json; charset=utf-8',
response=json.dumps(data),
)
def invalid_response(typ, message=None, status=400):
"""Invalid Response
This will be the return value whenever the server runs into an error
either from the client or the server."""
return werkzeug.wrappers.Response(
status=status,
content_type='application/json; charset=utf-8',
response=json.dumps({
'type': typ,
'message': message if message else 'wrong arguments (missing validation)',
}),
)
def extract_arguments(payloads, offset=0, limit=0, order=None):
"""."""
fields, domain, payload = [], [], {}
data = str(payloads)[2:-2]
try:
payload = json.loads(data)
except JSONDecodeError as e:
pass
if payload.get('domain'):
for _domain in payload.get('domain'):
l, o, r = _domain
if o == "': '":
o = '='
elif o == "!': '":
o = '!='
domain.append(tuple([l, o, r]))
if payload.get('fields'):
fields += payload.get('fields')
if payload.get('offset'):
offset = int(payload['offset'])
if payload.get('limit'):
limit = int(payload.get('limit'))
if payload.get('order'):
order = payload.get('order')
return [domain, fields, offset, limit, order]
def validate_token(func):
"""."""
@functools.wraps(func)
def wrap(self, *args, **kwargs):
"""."""
access_token = request.httprequest.headers.get('access_token')
if not access_token:
return invalid_response('access_token_not_found', 'missing access token in request header', 401)
access_token_data = request.env['api.access_token'].sudo().search(
[('token', '=', access_token)], order='id DESC', limit=1)
if access_token_data.find_one_or_create_token(user_id=access_token_data.user_id.id) != access_token:
return invalid_response('access_token', 'token seems to have expired or invalid', 401)
request.session.uid = access_token_data.user_id.id
request.uid = access_token_data.user_id.id
return func(self, *args, **kwargs)
return wrap
_routes = [
'/api/<model>',
'/api/<model>/<id>',
'/api/<model>/<id>/<action>'
]
class APIController(http.Controller):
"""."""
def __init__(self):
self._model = 'ir.model'
@validate_token
@http.route(_routes, type='http', auth="none", methods=['GET'], csrf=False)
def get(self, model=None, id=None, **payload):
ioc_name = model
model = request.env[self._model].sudo().search(
[('model', '=', model)], limit=1)
if model:
domain, fields, offset, limit, order = extract_arguments(
payload)
data = request.env[model.model].sudo().search_read(
domain=domain, fields=fields, offset=offset, limit=limit, order=order)
if id:
domain = [('id', '=', int(id))]
data = request.env[model.model].sudo().search_read(
domain=domain, fields=fields, offset=offset, limit=limit, order=order)
if data:
return valid_response(data)
else:
return valid_response(data)
return invalid_response('invalid object model', 'The model %s is not available in the registry.' % ioc_name)
@validate_token
@http.route(_routes, type='http', auth="none", methods=['POST'], csrf=False)
def create(self, model=None, id=None, **payload):
"""Create a new record.
Basic sage:
import requests
headers = {
'content-type': 'application/x-www-form-urlencoded',
'charset': 'utf-8',
'access-token': 'access_token'
}
data = {
'name': 'Babatope Ajepe',
'country_id': 105,
'child_ids': [
{
'name': 'Contact',
'type': 'contact'
},
{
'name': 'Invoice',
'type': 'invoice'
}
],
'category_id': [{'id': 9}, {'id': 10}]
}
req = requests.post('%s/api/res.partner/' %
base_url, headers=headers, data=data)
"""
ioc_name = model
model = request.env[self._model].sudo().search(
[('model', '=', model)], limit=1)
if model:
try:
resource = request.env[model.model].sudo().create(payload)
except Exception as e:
return invalid_response('params', e)
else:
data = {'id': resource.id}
if resource:
return valid_response(data)
else:
return valid_response(data)
return invalid_response('invalid object model', 'The model %s is not available in the registry.' % ioc_name)
@validate_token
@http.route(_routes, type='http', auth="none", methods=['PUT'], csrf=False)
def put(self, model=None, id=None, **payload):
"""."""
try:
_id = int(id)
except Exception as e:
return invalid_response('invalid object id', 'invalid literal %s for id with base ' % id)
_model = request.env[self._model].sudo().search(
[('model', '=', model)], limit=1)
if not _model:
return invalid_response('invalid object model', 'The model %s is not available in the registry.' % model, 404)
try:
request.env[_model.model].sudo().browse(_id).write(payload)
except Exception as e:
return invalid_response('exception', e.name)
else:
return valid_response('update %s record with id %s successfully!' % (_model.model, _id))
@validate_token
@http.route(_routes, type='http', auth="none", methods=['DELETE'], csrf=False)
def delete(self, model=None, id=None, **payload):
"""."""
try:
_id = int(id)
except Exception as e:
return invalid_response('invalid object id', 'invalid literal %s for id with base ' % id)
try:
record = request.env[model].sudo().search([('id', '=', _id)])
if record:
record.unlink()
else:
return invalid_response('missing_record', 'record object with id %s could not be found' % _id, 404)
except Exception as e:
return invalid_response('exception', e.name, 503)
else:
return valid_response('record %s has been successfully deleted' % record.id)
@validate_token
@http.route(_routes, type='http', auth="none", methods=['PATCH'], csrf=False)
def patch(self, model=None, id=None, action=None, **payload):
"""."""
try:
_id = int(id)
except Exception as e:
return invalid_response('invalid object id', 'invalid literal %s for id with base ' % id)
try:
record = request.env[model].sudo().search([('id', '=', _id)])
_callable = action in [method for method in dir(
record) if callable(getattr(record, method))]
if record and _callable:
# action is a dynamic variable.
getattr(record, action)()
else:
return invalid_response('missing_record',
'record object with id %s could not be found or %s object has no method %s' % (_id, model, action), 404)
except Exception as e:
return invalid_response('exception', e, 503)
else:
return valid_response('record %s has been successfully patched' % record.id)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment