Skip to content
Permalink
Browse files

Merge remote-tracking branch 'upstream/11.0' into 12.0-automerge-4e7b…

…a90eecb78cb4ca71b5f0486d9a1973a6f246
  • Loading branch information...
root
root committed Aug 20, 2019
2 parents 85611b2 + 4e7ba90 commit 462923030c06198129d5c6e480debf98a2f1171d
Showing with 4,007 additions and 5 deletions.
  1. +2 −2 base_automation_webhook/__manifest__.py
  2. +3 −3 base_automation_webhook/static/description/index.html
  3. +87 −0 openapi/README.rst
  4. +7 −0 openapi/__init__.py
  5. +49 −0 openapi/__manifest__.py
  6. +6 −0 openapi/controllers/__init__.py
  7. +193 −0 openapi/controllers/api.py
  8. +209 −0 openapi/controllers/apijsonrequest.py
  9. +77 −0 openapi/controllers/main.py
  10. +1,143 −0 openapi/controllers/pinguin.py
  11. +62 −0 openapi/data/openapi_demo.xml
  12. +4 −0 openapi/doc/changelog.rst
  13. +83 −0 openapi/doc/index.rst
  14. BIN openapi/images/openapi-swagger.png
  15. +7 −0 openapi/models/__init__.py
  16. +30 −0 openapi/models/ir_exports.py
  17. +16 −0 openapi/models/ir_model.py
  18. +546 −0 openapi/models/openapi_access.py
  19. +16 −0 openapi/models/openapi_log.py
  20. +230 −0 openapi/models/openapi_namespace.py
  21. +33 −0 openapi/models/res_users.py
  22. +5 −0 openapi/security/ir.model.access.csv
  23. BIN openapi/static/description/aws.png
  24. BIN openapi/static/description/dataiku.png
  25. BIN openapi/static/description/dots.jpg
  26. BIN openapi/static/description/icon.png
  27. +348 −0 openapi/static/description/index.html
  28. BIN openapi/static/description/openapi-configuration-model.png
  29. BIN openapi/static/description/openapi-configuration-namespace.png
  30. BIN openapi/static/description/openapi-logs.png
  31. BIN openapi/static/description/powerbi.png
  32. BIN openapi/static/description/swagger-editor.png
  33. BIN openapi/static/description/syndesis.jpg
  34. +45 −0 openapi/static/src/js/configure_api_button.js
  35. +71 −0 openapi/static/src/js/dashboard.js
  36. +32 −0 openapi/static/src/js/dashboard.tour.js
  37. +20 −0 openapi/static/src/xml/configure_api_button.xml
  38. +44 −0 openapi/static/src/xml/dashboard.xml
  39. +3 −0 openapi/tests/__init__.py
  40. +248 −0 openapi/tests/test_api.py
  41. +40 −0 openapi/tests/test_dashboard.py
  42. +30 −0 openapi/tests/test_json_spec.py
  43. +12 −0 openapi/views/assets.xml
  44. +10 −0 openapi/views/assets_demo.xml
  45. +21 −0 openapi/views/ir_model_view.xml
  46. +246 −0 openapi/views/openapi_view.xml
  47. +27 −0 openapi/views/res_users_view.xml
  48. +2 −0 requirements.txt
@@ -10,8 +10,8 @@
"application": False,

"author": "IT-Projects LLC, Ivan Yelizariev",
"support": "apps@it-projects.info",
"website": "https://apps.odoo.com/apps/modules/10.0/base_automation_webhook/",
"support": "sync@it-projects.info",
"website": "https://apps.odoo.com/apps/modules/11.0/base_automation_webhook/",
"license": "LGPL-3",
"price": 60.00,
"currency": "EUR",
@@ -118,7 +118,7 @@ <h3 class="oe_slogan">This simple module opens you a door for infinite integrati
<div class="oe_span12">

<div class="alert alert-warning oe_centered" align="center" style="padding:1.3em 0.6em; font-size: 150%;">
<b>Are you not a developer?</b> <br/>Send this page to your IT guys to evaluate the possibilities or tell us your concerns
<b>Are you not not very much aware of webhooks?</b> <br/>You may send this page to your IT guys to evaluate the possibilities or tell us your concerns
</div>

</div>
@@ -129,9 +129,9 @@ <h3 class="oe_slogan">This simple module opens you a door for infinite integrati
<div class="oe_row oe_spaced">
<div class="oe_span8">
<h2>Have a question?</h2>
<p class="oe_mt32">Contact us by <a href="mailto:apps@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p>
<p class="oe_mt32">Contact us by <a href="mailto:sync@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p>
<ul>
<li><a href="mailto:apps@it-projects.info">apps@it-projects.info <i class="fa fa-envelope-o"></i></a></li>
<li><a href="mailto:sync@it-projects.info">sync@it-projects.info <i class="fa fa-envelope-o"></i></a></li>
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li>
<li><a href="https://m.me/itprojectsllc" target="_blank">https://m.me/itprojectsllc <i class="fa fa-facebook-square"></i></a></li>
<li>skype@it-projects.info <i class="fa fa-skype"></i></li>
@@ -0,0 +1,87 @@
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.png
:target: https://www.gnu.org/licenses/lgpl
:alt: License: LGPL-3

==========================
REST API/OpenAPI/Swagger
==========================

Set up REST API and export OpenAPI (Swagger) specification file for integration
with whatever system you need. All can be configured in Odoo UI, no extra module
is needed.

This module implements a ``/api/v1/`` route tree.

Authentication
==============

* Database inference: Database name encoded and appended to the token
* User authentication through the actual token

As a workaround for multi-db Odoo instances, system uses *Basic Authentication* with
``db_name:token`` credentials, where ``token`` is a new field in ``res.users``
model. That is, whenever you see Username / Password to setup OpenAPI
connection, use Database Name / OpenAPI toekn accordingly.

Roadmap
=======

* TODO: Add a smart button for Logs in ``openapi.namespace`` form
* TODO: Add a button to developer menu to grant access to current model

* `Activate Developer Mode <https://odoo-development.readthedocs.io/en/latest/odoo/usage/debug-mode.html>`__
* Open the developer tools drop down
* Click menu ``Configure REST API`` located within the dropdown
* On the form that opens, activate and configure this module for REST API accessability.
* Click ``[Apply]``

* TODO: when user is not authenticated api returns 200 with the message below, instead of designed 401

::

File "/opt/odoo/vendor/it-projects-llc/sync-addons/openapi/controllers/pinguin.py", line 152, in authenticate_token_for_user
raise werkzeug.exceptions.HTTPException(response=error_response(*CODE__no_user_auth))
HTTPException: ??? Unknown Error: None

* TODO: ``wrap__resource__create_one`` method makes ``cr.commit()``. We need to avoid that.
* TODO: add code examples for other programming languages in index.html. The examples can be based on generated swagger clients. The idea of the scripts must be the same as for python (search for partner, create if it doesn't exist, send message)

Credits
=======

Contributors
------------
* `David Arnold <dar@xoe.solutions>`__
* `Ivan Yelizariev <https://it-projects.info/team/yelizariev>`__
* `Rafis Bikbov <https://it-projects.info/team/RafiZz>`__
* `Stanislav Krotov <https://it-projects.info/team/ufaks>`__

Sponsors
--------
* `IT-Projects LLC <https://it-projects.info>`__
* `XOE Solutions <https://xoe.solutions>`__

Maintainers
-----------
* `IT-Projects LLC <https://it-projects.info>`__

To get a guaranteed support you are kindly requested to purchase the module at `odoo apps store <https://apps.odoo.com/apps/modules/11.0/openapi/>`__.

Thank you for understanding!

`IT-Projects Team <https://www.it-projects.info/team>`__

Further information
===================

Demo: http://runbot.it-projects.info/demo/sync-addons/11.0

HTML Description: https://apps.odoo.com/apps/modules/11.0/openapi/

Usage instructions: `<doc/index.rst>`_

Changelog: `<doc/changelog.rst>`_

Notifications on updates: `via Atom <https://github.com/it-projects-llc/sync-addons/commits/11.0/openapi.atom>`_, `by Email <https://blogtrottr.com/?subscribe=https://github.com/it-projects-llc/sync-addons/commits/11.0/openapi.atom>`_

Tested on Odoo 11.0 855447fc77fdbe2972be043b7ba9131567d21b9f
@@ -0,0 +1,7 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

def post_load():
# make import in post_load to avoid applying monkey patches when this
# module is not installed
from . import models
from . import controllers
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Copyright 2018-2019 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
"name": """REST API/OpenAPI/Swagger""",
"summary": """API to integrate Odoo with whatever system you need""",
"category": "",
# "live_test_url": "",
"images": ['images/openapi-swagger.png'],
"version": "11.0.1.0.0",
"application": False,

"author": "IT-Projects LLC, Ivan Yelizariev",
"support": "sync@it-projects.info",
"website": "https://apps.odoo.com/apps/modules/10.0/openapi/",
"license": "LGPL-3",
"price": 180.00,
"currency": "EUR",

"depends": [
"web_tour",
"web_settings_dashboard",
],
"external_dependencies": {"python": ['bravado_core', 'swagger_spec_validator'], "bin": []},
"data": [
"security/ir.model.access.csv",
"views/assets.xml",
"views/openapi_view.xml",
"views/res_users_view.xml",
"views/ir_model_view.xml",
],
"demo": [
"views/assets_demo.xml",
"data/openapi_demo.xml",
],
"qweb": [
"static/src/xml/dashboard.xml",
# Сommented until we discuss it
# "static/src/xml/configure_api_button.xml"
],

"post_load": "post_load",
"pre_init_hook": None,
"post_init_hook": None,
"uninstall_hook": None,

"auto_install": False,
"installable": True,
}
@@ -0,0 +1,6 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from . import apijsonrequest
from . import main
from . import api
from . import pinguin
@@ -0,0 +1,193 @@
# -*- coding: utf-8 -*-
# Copyright 2018, XOE Solutions
# Copyright 2019 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2018 Rafis Bikbov <https://it-projects.info/team/bikbov>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
# pylint: disable=redefined-builtin
import logging

from odoo import http

from . import pinguin

_logger = logging.getLogger(__name__)

#################################################################
# Odoo REST API #
# Version 1 #
# --------------------------------------------------------------#
# The current api version is considered stable, although #
# the exposed models and methods change as they are configured #
# on the database level. Only if significant changes in the api #
# generation logic should be implemented in the future #
# a version bump should be considered. #
#################################################################

API_ENDPOINT = '/api'
API_ENDPOINT_V1 = '/v1'
# API_ENDPOINT_V2 = '/v2'

# We patch the route decorator in pinguin.py
# with authentication and DB inference logic.
# We also check if the model is installed in the database.
# Furthermore we check if api version is supported.
# This keeps the code below minial and readable.


class ApiV1Controller(http.Controller):
""" Implements the REST API V1 endpoint.
.. methods:
CRUD Methods:
- `POST .../<model>` -> `CreateOne`
- `PUT .../<model>/<id>` -> `UpdateOne`
- `GET .../<model>` -> `ReadMulti`
- `GET .../<model>/<id>` -> `ReadOne`
- `DELETE .../<model>/<id>` -> `UnlinkOne`
Auxiliary Methods:
- `PATCH .../<model>/<id>/<method>` -> `Call Method on Singleton Record`
- `PATCH .../<model>/<method>` -> `Call Method on RecordSet`
- `GET .../report/pdf/<report_external_id>` -> `Get Report as PDF`
- `GET .../report/html/<report_external_id>` -> `Get Report as HTML`
"""

_api_endpoint = API_ENDPOINT + API_ENDPOINT_V1
_api_endpoint = _api_endpoint + '/<namespace>'
# CreateOne # ReadMulti
_api_endpoint_model = _api_endpoint + '/<model>'
# ReadOne # UpdateOne # UnlinkOne
_api_endpoint_model_id = _api_endpoint + '/<model>/<int:id>'
# Call Method on Singleton Record
_api_endpoint_model_id_method = _api_endpoint + '/<model>/<int:id>/call/<method_name>'
# Call Method on RecordSet
_api_endpoint_model_method = _api_endpoint + '/<model>/call/<method_name>'
_api_endpoint_model_method_ids = _api_endpoint + '/<model>/call/<method_name>/<ids>'
# Get Reports
_api_report_docids = _api_endpoint + '/report/<any(pdf, html):converter>/<report_external_id>/<docids>'

# #################
# # CRUD Methods ##
# #################

# CreateOne
@pinguin.route(
_api_endpoint_model,
methods=['POST'],
type='apijson',
auth='none',
csrf=False)
def create_one__POST(self, namespace, model, **data):
conf = pinguin.get_model_openapi_access(namespace, model)
pinguin.method_is_allowed('api_create', conf['method'], main=True, raise_exception=True)
# FIXME: What is contained in context and for what?
# # If context is not a python dict
# # TODO unwrap
# if isinstance(kw.get('context'), basestring):
# context = get_create_context(namespace, model, kw.get('context'))
# else:
# context = kw.get('context') or {}
return pinguin.wrap__resource__create_one(
modelname=model,
context=conf['context'],
data=data,
success_code=pinguin.CODE__created,
out_fields=conf['out_fields_read_one'])

# ReadMulti (optional: filters, offset, limit, order, include_fields, exclude_fields):
@pinguin.route(_api_endpoint_model, methods=['GET'], type='http', auth='none', csrf=False)
def read_multi__GET(self, namespace, model, **kw):
conf = pinguin.get_model_openapi_access(namespace, model)
pinguin.method_is_allowed('api_read', conf['method'], main=True, raise_exception=True)
return pinguin.wrap__resource__read_all(
modelname=model,
success_code=pinguin.CODE__success,
out_fields=conf['out_fields_read_multi'])

# ReadOne (optional: include_fields, exclude_fields)
@pinguin.route(
_api_endpoint_model_id, methods=['GET'], type='http', auth='none', csrf=False)
def read_one__GET(self, namespace, model, id, **kw):
conf = pinguin.get_model_openapi_access(namespace, model)
pinguin.method_is_allowed('api_read', conf['method'], main=True, raise_exception=True)
return pinguin.wrap__resource__read_one(
modelname=model,
id=id,
success_code=pinguin.CODE__success,
out_fields=conf['out_fields_read_one'])

# UpdateOne
@pinguin.route(
_api_endpoint_model_id,
methods=['PUT'],
type='apijson',
auth='none',
csrf=False)
def update_one__PUT(self, namespace, model, id, **data):
conf = pinguin.get_model_openapi_access(namespace, model)
pinguin.method_is_allowed('api_update', conf['method'], main=True, raise_exception=True)
return pinguin.wrap__resource__update_one(
modelname=model, id=id, success_code=pinguin.CODE__ok_no_content, data=data)

# UnlinkOne
@pinguin.route(
_api_endpoint_model_id,
methods=['DELETE'],
type='http',
auth='none',
csrf=False)
def unlink_one__DELETE(self, namespace, model, id, **data):
conf = pinguin.get_model_openapi_access(namespace, model)
pinguin.method_is_allowed('api_delete', conf['method'], main=True, raise_exception=True)
return pinguin.wrap__resource__unlink_one(
modelname=model, id=id, success_code=pinguin.CODE__ok_no_content)

# ######################
# # Auxiliary Methods ##
# ######################

# Call Method on Singleton Record (optional: method parameters)
@pinguin.route(
_api_endpoint_model_id_method,
methods=['PATCH'],
type='apijson',
auth='none',
csrf=False)
def call_method_one__PATCH(self, namespace, model, id, method_name, **method_params):
conf = pinguin.get_model_openapi_access(namespace, model)
pinguin.method_is_allowed(method_name, conf['method'])
return pinguin.wrap__resource__call_method(
modelname=model,
ids=[id],
method=method_name,
method_params=method_params,
success_code=pinguin.CODE__success)

# Call Method on RecordSet (optional: method parameters)
@pinguin.route(
[_api_endpoint_model_method,
_api_endpoint_model_method_ids],
methods=['PATCH'],
type='apijson',
auth='none',
csrf=False)
def call_method_multi__PATCH(self, namespace, model, method_name, ids=None, **method_params):
conf = pinguin.get_model_openapi_access(namespace, model)
pinguin.method_is_allowed(method_name, conf['method'])
ids = ids and ids.split(',') or []
return pinguin.wrap__resource__call_method(
modelname=model,
ids=ids,
method=method_name,
method_params=method_params,
success_code=pinguin.CODE__success)

# Get Report
@pinguin.route(_api_report_docids, methods=['GET'], type='http', auth='none', csrf=False)
def report__GET(self, converter, namespace, report_external_id, docids):
return pinguin.wrap__resource__get_report(
namespace=namespace,
report_external_id=report_external_id,
docids=docids,
converter=converter,
success_code=pinguin.CODE__success)

0 comments on commit 4629230

Please sign in to comment.
You can’t perform that action at this time.