[ADD]REST API for flectra
This commit is contained in:
parent
eccaf76fa0
commit
a84a9fce36
5
addons/rest_api/__init__.py
Executable file
5
addons/rest_api/__init__.py
Executable file
@ -0,0 +1,5 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import rest_exception
|
28
addons/rest_api/__manifest__.py
Executable file
28
addons/rest_api/__manifest__.py
Executable file
@ -0,0 +1,28 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'REST API For Flectra',
|
||||
'version': '1.0.0',
|
||||
'category': 'API',
|
||||
'author': 'FlectraHQ',
|
||||
'website': 'https://www.flectrahq.com',
|
||||
'summary': 'REST API For Flectra',
|
||||
'description': """
|
||||
REST API For Flectra
|
||||
====================
|
||||
With use of this module user can enable REST API in any Flectra applications/modules
|
||||
|
||||
For detailed example of REST API refer *readme.md*
|
||||
""",
|
||||
'depends': [
|
||||
'web',
|
||||
],
|
||||
'data': [
|
||||
'data/ir_configparameter_data.xml',
|
||||
'views/ir_model_view.xml',
|
||||
'views/res_user_view.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
3
addons/rest_api/controllers/__init__.py
Executable file
3
addons/rest_api/controllers/__init__.py
Executable file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import main
|
339
addons/rest_api/controllers/main.py
Executable file
339
addons/rest_api/controllers/main.py
Executable file
@ -0,0 +1,339 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import functools
|
||||
import hashlib
|
||||
import os
|
||||
import werkzeug.wrappers
|
||||
import ast
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import flectra
|
||||
from flectra import http
|
||||
from flectra.http import request
|
||||
from flectra import fields
|
||||
from ..rest_exception import *
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def eval_json_to_data(modelname, json_data, create=True):
|
||||
Model = request.env[modelname]
|
||||
model_fiels = Model._fields
|
||||
field_name = [name for name, field in Model._fields.items()]
|
||||
values = {}
|
||||
for field in json_data:
|
||||
if field not in field_name:
|
||||
continue
|
||||
if field not in field_name:
|
||||
continue
|
||||
val = json_data[field]
|
||||
if not isinstance(val, list):
|
||||
values[field] = val
|
||||
else:
|
||||
values[field] = []
|
||||
if not create and isinstance(model_fiels[field], fields.Many2many):
|
||||
values[field].append((5,))
|
||||
for res in val:
|
||||
recored = {}
|
||||
for f in res:
|
||||
recored[f] = res[f]
|
||||
if isinstance(model_fiels[field], fields.Many2many):
|
||||
values[field].append((4, recored['id']))
|
||||
|
||||
elif isinstance(model_fiels[field], flectra.fields.One2many):
|
||||
if create:
|
||||
values[field].append((0, 0, recored))
|
||||
else:
|
||||
if 'id' in recored:
|
||||
id = recored['id']
|
||||
del recored['id']
|
||||
values[field].append((1, id, recored)) if len(recored) else values[field].append((2, id))
|
||||
else:
|
||||
values[field].append((0, 0, recored))
|
||||
return values
|
||||
|
||||
|
||||
def object_read(modelname, default_domain, status_code,
|
||||
post={}):
|
||||
json_data = post
|
||||
domain = default_domain or []
|
||||
field = []
|
||||
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
|
||||
})
|
||||
|
||||
|
||||
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)])
|
||||
if data:
|
||||
return valid_response(status_code, data)
|
||||
else:
|
||||
return object_not_found()
|
||||
|
||||
|
||||
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)
|
||||
try:
|
||||
res = request.env[modelname].create(vals)
|
||||
flectra_error = ''
|
||||
except Exception as e:
|
||||
res = None
|
||||
flectra_error = 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):
|
||||
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 = ''
|
||||
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)
|
||||
|
||||
|
||||
def object_delete_one(modelname, id, status_code):
|
||||
try:
|
||||
id = int(id)
|
||||
except:
|
||||
id = None
|
||||
if not id:
|
||||
return invalid_object_id()
|
||||
try:
|
||||
res = request.env[modelname].browse(id).unlink()
|
||||
flectra_error = ''
|
||||
except Exception as e:
|
||||
res = None
|
||||
flectra_error = e
|
||||
if res:
|
||||
return valid_response(status_code, 'Record Successfully '
|
||||
'Deleted!')
|
||||
else:
|
||||
return no_object_deleted(flectra_error)
|
||||
|
||||
|
||||
def check_valid_token(func):
|
||||
@functools.wraps(func)
|
||||
def wrap(self, *args, **kwargs):
|
||||
access_token = request.httprequest.headers.get('access_token')
|
||||
if not access_token:
|
||||
info = "Missing access token in request header!"
|
||||
error = 'access_token_not_found'
|
||||
_logger.error(info)
|
||||
return invalid_response(400, error, info)
|
||||
|
||||
access_token_data = request.env['oauth.access_token'].sudo().search(
|
||||
[('token', '=', access_token)], order='id DESC', limit=1)
|
||||
|
||||
if access_token_data._get_access_token(user_id=access_token_data.user_id.id) != access_token:
|
||||
return invalid_token()
|
||||
|
||||
request.session.uid = access_token_data.user_id.id
|
||||
request.uid = access_token_data.user_id.id
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
def generate_token(length=40):
|
||||
random_data = os.urandom(100)
|
||||
hash_gen = hashlib.new('sha512')
|
||||
hash_gen.update(random_data)
|
||||
return hash_gen.hexdigest()[:length]
|
||||
|
||||
|
||||
# Read OAuth2 constants and setup token store:
|
||||
db_name = flectra.tools.config.get('db_name')
|
||||
if not db_name:
|
||||
_logger.warning("Warning: To proper setup OAuth - it's necessary to "
|
||||
"set the parameter 'db_name' in flectra config file!")
|
||||
|
||||
|
||||
# List of REST resources in current file:
|
||||
# (url prefix) (method) (action)
|
||||
# /api/auth/get_tokens POST - Login in flectra and get access tokens
|
||||
# /api/auth/delete_tokens POST - Delete access tokens from token store
|
||||
|
||||
|
||||
# HTTP controller of REST resources:
|
||||
|
||||
class ControllerREST(http.Controller):
|
||||
|
||||
# Login in flectra database and get access tokens:
|
||||
@http.route('/api/auth/get_tokens', methods=['POST'], type='http',
|
||||
auth='none', csrf=False)
|
||||
def api_auth_gettokens(self, **post):
|
||||
# Convert http data into json:
|
||||
db = post['db'] if post.get('db') else None
|
||||
username = post['username'] if post.get('username') else None
|
||||
password = post['password'] if post.get('password') else None
|
||||
# Compare dbname (from HTTP-request vs. flectra config):
|
||||
if db and (db != db_name):
|
||||
info = "Wrong 'dbname'!"
|
||||
error = 'wrong_dbname'
|
||||
_logger.error(info)
|
||||
return invalid_response(400, error, info)
|
||||
|
||||
# Empty 'db' or 'username' or 'password:
|
||||
if not db or not username or not password:
|
||||
info = "Empty value of 'db' or 'username' or 'password'!"
|
||||
error = 'empty_db_or_username_or_password'
|
||||
_logger.error(info)
|
||||
return invalid_response(400, error, info)
|
||||
# Login in flectra database:
|
||||
try:
|
||||
request.session.authenticate(db, username, password)
|
||||
except:
|
||||
# Invalid database:
|
||||
info = "Invalid database!"
|
||||
error = 'invalid_database'
|
||||
_logger.error(info)
|
||||
return invalid_response(400, error, info)
|
||||
|
||||
uid = request.session.uid
|
||||
# flectra login failed:
|
||||
if not uid:
|
||||
info = "flectra User authentication failed!"
|
||||
error = 'flectra_user_authentication_failed'
|
||||
_logger.error(info)
|
||||
return invalid_response(401, error, info)
|
||||
|
||||
# Generate tokens
|
||||
access_token = request.env['oauth.access_token']._get_access_token(user_id = uid, create = True)
|
||||
|
||||
# Save all tokens in store
|
||||
_logger.info("Save OAuth2 tokens of user in store...")
|
||||
|
||||
# Successful response:
|
||||
return werkzeug.wrappers.Response(
|
||||
status=200,
|
||||
content_type='application/json; charset=utf-8',
|
||||
headers=[('Cache-Control', 'no-store'),
|
||||
('Pragma', 'no-cache')],
|
||||
response=json.dumps({
|
||||
'uid': uid,
|
||||
'user_context': request.session.get_context() if uid else {},
|
||||
'company_id': request.env.user.company_id.id if uid else 'null',
|
||||
'access_token': access_token,
|
||||
'expires_in': request.env.ref('rest_api.oauth2_access_token_expires_in').sudo().value,
|
||||
}),
|
||||
)
|
||||
|
||||
# Delete access tokens from token store:
|
||||
@http.route('/api/auth/delete_tokens', methods=['POST'], type='http',
|
||||
auth='none', csrf=False)
|
||||
def api_auth_deletetokens(self, **post):
|
||||
# Try convert http data into json:
|
||||
access_token = request.httprequest.headers.get('access_token')
|
||||
access_token_data = request.env['oauth.access_token'].sudo().search(
|
||||
[('token', '=', access_token)], order='id DESC', limit=1)
|
||||
|
||||
if not access_token_data:
|
||||
info = "No access token was provided in request!"
|
||||
error = 'no_access_token'
|
||||
_logger.error(info)
|
||||
return invalid_response(400, error, info)
|
||||
access_token_data.sudo().unlink()
|
||||
# Successful response:
|
||||
return valid_response(
|
||||
200,
|
||||
{}
|
||||
)
|
||||
|
||||
@http.route([
|
||||
'/api/<model_name>',
|
||||
'/api/<model_name>/<id>'
|
||||
], type='http', auth="none", methods=['POST', 'GET', 'PUT', 'DELETE'],
|
||||
csrf=False)
|
||||
@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()
|
||||
|
||||
def get_data(self, model_name=False, id=False, **post):
|
||||
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
|
||||
)
|
||||
|
||||
def put_data(self, model_name=False, id=False, **post):
|
||||
return object_update_one(
|
||||
modelname=model_name,
|
||||
id=id,
|
||||
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 delete_data(self, model_name=False, id=False):
|
||||
return object_delete_one(
|
||||
modelname=model_name,
|
||||
id=id,
|
||||
status_code=200
|
||||
)
|
11
addons/rest_api/data/ir_configparameter_data.xml
Executable file
11
addons/rest_api/data/ir_configparameter_data.xml
Executable file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- OAuth2 tokens time to live: -->
|
||||
<record id="oauth2_access_token_expires_in" model="ir.config_parameter">
|
||||
<field name="key">oauth2_access_token_expires_in</field>
|
||||
<field name="value">600</field>
|
||||
</record>
|
||||
</data>
|
||||
</flectra>
|
5
addons/rest_api/models/__init__.py
Normal file
5
addons/rest_api/models/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from .import ir_model
|
||||
from .import oauth_provider
|
11
addons/rest_api/models/ir_model.py
Normal file
11
addons/rest_api/models/ir_model.py
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import api, fields, models, modules, _
|
||||
|
||||
|
||||
class IrModel(models.Model):
|
||||
_inherit = 'ir.model'
|
||||
|
||||
rest_api = fields.Boolean('REST API', default=True,
|
||||
help="Enable REST API for this object/model")
|
80
addons/rest_api/models/oauth_provider.py
Normal file
80
addons/rest_api/models/oauth_provider.py
Normal file
@ -0,0 +1,80 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Author: Ivan Yelizariev, Ildar
|
||||
# Ref. from: https://github.com/it-projects-llc/odoo-saas-tools/blob/10.0/oauth_provider/models/oauth_provider.py
|
||||
|
||||
from flectra import models, fields, api
|
||||
from datetime import datetime, timedelta
|
||||
from flectra.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
try:
|
||||
from oauthlib import common as oauthlib_common
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class OauthAccessToken(models.Model):
|
||||
_name = 'oauth.access_token'
|
||||
|
||||
token = fields.Char('Access Token', required=True)
|
||||
user_id = fields.Many2one('res.users', string='User', required=True)
|
||||
expires = fields.Datetime('Expires', required=True)
|
||||
scope = fields.Char('Scope')
|
||||
|
||||
@api.multi
|
||||
def _get_access_token(self, user_id=None, create=False):
|
||||
if not user_id:
|
||||
user_id = self.env.user.id
|
||||
|
||||
access_token = self.env['oauth.access_token'].sudo().search(
|
||||
[('user_id', '=', user_id)], order='id DESC', limit=1)
|
||||
if access_token:
|
||||
access_token = access_token[0]
|
||||
if access_token.is_expired():
|
||||
access_token = None
|
||||
if not access_token and create:
|
||||
expires = datetime.now() + timedelta(seconds=int(self.env.ref('rest_api.oauth2_access_token_expires_in').sudo().value))
|
||||
vals = {
|
||||
'user_id': user_id,
|
||||
'scope': 'userinfo',
|
||||
'expires': expires.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'token': oauthlib_common.generate_token(),
|
||||
}
|
||||
access_token = self.env['oauth.access_token'].sudo().create(vals)
|
||||
# we have to commit now, because /oauth2/tokeninfo could
|
||||
# be called before we finish current transaction.
|
||||
self._cr.commit()
|
||||
if not access_token:
|
||||
return None
|
||||
return access_token.token
|
||||
|
||||
@api.multi
|
||||
def is_valid(self, scopes=None):
|
||||
"""
|
||||
Checks if the access token is valid.
|
||||
|
||||
:param scopes: An iterable containing the scopes to check or None
|
||||
"""
|
||||
self.ensure_one()
|
||||
return not self.is_expired() and self._allow_scopes(scopes)
|
||||
|
||||
@api.multi
|
||||
def is_expired(self):
|
||||
self.ensure_one()
|
||||
return datetime.now() > fields.Datetime.from_string(self.expires)
|
||||
|
||||
@api.multi
|
||||
def _allow_scopes(self, scopes):
|
||||
self.ensure_one()
|
||||
if not scopes:
|
||||
return True
|
||||
|
||||
provided_scopes = set(self.scope.split())
|
||||
resource_scopes = set(scopes)
|
||||
|
||||
return resource_scopes.issubset(provided_scopes)
|
||||
|
||||
|
||||
class Users(models.Model):
|
||||
_inherit = 'res.users'
|
||||
token_ids = fields.One2many('oauth.access_token', 'user_id',
|
||||
string="Access Tokens")
|
60
addons/rest_api/readme.md
Normal file
60
addons/rest_api/readme.md
Normal file
@ -0,0 +1,60 @@
|
||||
# REST API For Flectra
|
||||
|
||||
This module enable REST API in any Flectra applications/modules.
|
||||
|
||||
|
||||
## How to Use
|
||||
|
||||
|
||||
```python
|
||||
import requests, json, pprint
|
||||
|
||||
data = {'username': 'admin', 'password': 'admin', 'db': 'db_flectra_base'}
|
||||
s = requests.post('http://localhost:7073/api/auth/get_tokens', data=data)
|
||||
content = json.loads(s.content.decode('utf-8'))
|
||||
headers = {'access_token': content.get('access_token')}
|
||||
```
|
||||
|
||||
**GET request**
|
||||
```python
|
||||
p = requests.get('http://localhost:7073/api/res.partner/', headers=headers,
|
||||
data=json.dumps({'limit': 2}))
|
||||
# ***Pass optional parameter like this***
|
||||
# {
|
||||
# 'limit': 10, 'filters': "[('supplier','=',True),('parent_id','=', False)]",
|
||||
# 'order': 'name asc', 'offset': 10
|
||||
# }
|
||||
print(p.content)
|
||||
```
|
||||
|
||||
**POST request**
|
||||
```python
|
||||
p = requests.post('http://localhost:7073/api/res.partner/', headers=headers,
|
||||
data=json.dumps({
|
||||
'name':'John',
|
||||
'country_id': 105,
|
||||
'child_ids': [{'name': 'Contact', 'type':'contact'},
|
||||
{'name': 'Invoice', 'type':'invoice'}],
|
||||
'category_id': [{'id':9}, {'id': 10}]
|
||||
}
|
||||
))
|
||||
print(p.content)
|
||||
```
|
||||
|
||||
**PUT Request**
|
||||
```python
|
||||
p = requests.put('http://localhost:7073/api/res.partner/68', headers=headers,
|
||||
data=json.dumps({
|
||||
'name':'John Doe',
|
||||
'country_id': 107,
|
||||
'category_id': [{'id': 10}]
|
||||
}
|
||||
))
|
||||
print(p.content)
|
||||
```
|
||||
|
||||
**DELETE Request**
|
||||
```python
|
||||
p = requests.delete('http://localhost:7073/api/res.partner/68', headers=headers)
|
||||
print(p.content)
|
||||
```
|
73
addons/rest_api/rest_exception.py
Normal file
73
addons/rest_api/rest_exception.py
Normal file
@ -0,0 +1,73 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
import werkzeug.wrappers
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def valid_response(status, data):
|
||||
return werkzeug.wrappers.Response(
|
||||
status=status,
|
||||
content_type='application/json; charset=utf-8',
|
||||
response=json.dumps(data),
|
||||
)
|
||||
|
||||
|
||||
def invalid_response(status, error, info):
|
||||
return werkzeug.wrappers.Response(
|
||||
status=status,
|
||||
content_type='application/json; charset=utf-8',
|
||||
response=json.dumps({
|
||||
'error': error,
|
||||
'error_descrip': info,
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
def invalid_object_id():
|
||||
_logger.error("Invalid object 'id'!")
|
||||
return invalid_response(400, 'invalid_object_id', "Invalid object 'id'!")
|
||||
|
||||
|
||||
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():
|
||||
_logger.error("Not found object(s) in flectra!")
|
||||
return invalid_response(404, 'not_found_object_in_flectra',
|
||||
"Not found object(s) in flectra!")
|
||||
|
||||
|
||||
def unable_delete():
|
||||
_logger.error("Access Denied!")
|
||||
return invalid_response(404, "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',
|
||||
"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" %
|
||||
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',
|
||||
"Not deleted object in flectra! ERROR: %s" %
|
||||
flectra_error)
|
2
addons/rest_api/security/ir.model.access.csv
Normal file
2
addons/rest_api/security/ir.model.access.csv
Normal file
@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_oauth_access_token,access_oauth_access_token,model_oauth_access_token,,1,1,1,1
|
|
23
addons/rest_api/views/ir_model_view.xml
Normal file
23
addons/rest_api/views/ir_model_view.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
|
||||
<record id="view_model_form_inherit" model="ir.ui.view">
|
||||
<field name="model">ir.model</field>
|
||||
<field name="inherit_id" ref="base.view_model_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="transient" position="after">
|
||||
<field name="rest_api"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_model_tree_inherit" model="ir.ui.view">
|
||||
<field name="model">ir.model</field>
|
||||
<field name="inherit_id" ref="base.view_model_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="transient" position="after">
|
||||
<field name="rest_api"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
17
addons/rest_api/views/res_user_view.xml
Normal file
17
addons/rest_api/views/res_user_view.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
|
||||
<record id="view_users_form_inherit" model="ir.ui.view">
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//notebook/page" position="inside">
|
||||
<field name="token_ids">
|
||||
<tree create="false">
|
||||
<field name="token"/>
|
||||
</tree>
|
||||
</field>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
Loading…
Reference in New Issue
Block a user