Moisés López
8 years ago
committed by
Jairo Llopis
13 changed files with 648 additions and 0 deletions
-
143webhook/README.rst
-
7webhook/__init__.py
-
30webhook/__openerp__.py
-
5webhook/controllers/__init__.py
-
44webhook/controllers/main.py
-
16webhook/data/webhook_data.xml
-
17webhook/demo/webhook_demo.xml
-
5webhook/models/__init__.py
-
205webhook/models/webhook.py
-
3webhook/security/ir.model.access.csv
-
5webhook/tests/__init__.py
-
124webhook/tests/test_webhook_post.py
-
44webhook/views/webhook_views.xml
@ -0,0 +1,143 @@ |
|||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
======= |
||||
|
Webhook |
||||
|
======= |
||||
|
|
||||
|
Module to receive .. _global webhooks: https://en.wikipedia.org/wiki/Webhook events. |
||||
|
This module invoke methods to process webhook events. |
||||
|
|
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
|
||||
|
You will need create a new module to add your logic to process the events with methods called: |
||||
|
*def run_CONSUMER_EVENT\** |
||||
|
Example with gihub consumer and push event. |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
@api.one |
||||
|
def run_github_push_task(self): |
||||
|
# You will have all request data in |
||||
|
# variable: self.env.request |
||||
|
pass |
||||
|
|
||||
|
|
||||
|
Where CONSUMER is the name of you webhook consumer. e.g. github (Extract from field *name* of *webhook* model) |
||||
|
Where EVENT is the name of the event from webhook *request* data. |
||||
|
Where *\** is your particular method to process this event. |
||||
|
|
||||
|
To configure a new webhook you need add all ip or subnet address (with *ip/integer*) owned by your webhook consumer in webhook.address model as data. |
||||
|
|
||||
|
Example with github: |
||||
|
|
||||
|
.. code-block:: xml |
||||
|
|
||||
|
<!--webhook github data of remote address--> |
||||
|
<record model="webhook.address" id="webhook_address_github"> |
||||
|
<field name="name">192.30.252.0/22</field> |
||||
|
<field name="webhook_id" ref="webhook_github"/> |
||||
|
</record> |
||||
|
|
||||
|
|
||||
|
You need to add a python code to extract event name from webhook request info into `python_code_get_event` field of webhook model. |
||||
|
You can get all full data of request webhook from variable `request` |
||||
|
Example with github: |
||||
|
|
||||
|
.. code-block:: xml |
||||
|
|
||||
|
<!--webhook github data--> |
||||
|
<record model="webhook" id="webhook_github"> |
||||
|
<field name="name">github</field> |
||||
|
<field name="python_code_get_event">request.httprequest.headers.get('X-Github-Event')</field> |
||||
|
</record> |
||||
|
|
||||
|
|
||||
|
Full example of create a new webhook configuration data. |
||||
|
|
||||
|
.. code-block:: xml |
||||
|
|
||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<!--webhook github data--> |
||||
|
<record model="webhook" id="webhook_github"> |
||||
|
<field name="name">github</field> |
||||
|
<field name="python_code_get_event">request.httprequest.headers.get('X-Github-Event')</field> |
||||
|
</record> |
||||
|
<!--webhook github data of remote address--> |
||||
|
<record model="webhook.address" id="webhook_address_github"> |
||||
|
<field name="name">192.30.252.0/22</field> |
||||
|
<field name="webhook_id" ref="webhook_github"/> |
||||
|
</record> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
||||
|
|
||||
|
|
||||
|
.. figure:: path/to/local/image.png |
||||
|
:alt: alternative description |
||||
|
:width: 600 px |
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
To use this module, you need to: |
||||
|
|
||||
|
#. Go to your customer webhook configuration from 3rd-party applications |
||||
|
and use the odoo webhook url HOST/webhook/NAME_WEBHOOK |
||||
|
|
||||
|
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
||||
|
:alt: Try me on Runbot |
||||
|
:target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch} |
||||
|
|
||||
|
.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt |
||||
|
.. branch is "8.0" for example |
||||
|
|
||||
|
|
||||
|
Bug Tracker |
||||
|
=========== |
||||
|
|
||||
|
Bugs are tracked on `GitHub Issues |
||||
|
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please |
||||
|
check there if your issue has already been reported. If you spotted it first, |
||||
|
help us smash it by providing detailed and welcomed feedback. |
||||
|
|
||||
|
Credits |
||||
|
======= |
||||
|
|
||||
|
Images |
||||
|
------ |
||||
|
|
||||
|
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_. |
||||
|
|
||||
|
Contributors |
||||
|
------------ |
||||
|
|
||||
|
* Moisés López <moylop260@vauxoo.com> |
||||
|
|
||||
|
Funders |
||||
|
------- |
||||
|
|
||||
|
The development of this module has been financially supported by: |
||||
|
|
||||
|
* Vauxoo |
||||
|
|
||||
|
Maintainer |
||||
|
---------- |
||||
|
|
||||
|
.. image:: https://odoo-community.org/logo.png |
||||
|
:alt: Odoo Community Association |
||||
|
:target: https://odoo-community.org |
||||
|
|
||||
|
This module is maintained by the OCA. |
||||
|
|
||||
|
OCA, or the Odoo Community Association, is a nonprofit organization whose |
||||
|
mission is to support the collaborative development of Odoo features and |
||||
|
promote its widespread use. |
||||
|
|
||||
|
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1,7 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2016 Vauxoo - https://www.vauxoo.com/ |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
|
from . import models |
||||
|
from . import controllers |
||||
|
from . import tests |
@ -0,0 +1,30 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2016 Vauxoo - https://www.vauxoo.com/ |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
{ |
||||
|
'name': 'Webhook', |
||||
|
'version': '8.0.1.0.0', |
||||
|
'author': 'Vauxoo, Odoo Community Association (OCA)', |
||||
|
'category': 'Server Tools', |
||||
|
'website': 'https://www.vauxoo.com', |
||||
|
'license': 'AGPL-3', |
||||
|
'depends': [ |
||||
|
'web', |
||||
|
], |
||||
|
'external_dependencies': { |
||||
|
'python': [ |
||||
|
'ipaddress', |
||||
|
'requests', |
||||
|
], |
||||
|
}, |
||||
|
'data': [ |
||||
|
'security/ir.model.access.csv', |
||||
|
'views/webhook_views.xml', |
||||
|
'data/webhook_data.xml', |
||||
|
], |
||||
|
'demo': [ |
||||
|
'demo/webhook_demo.xml', |
||||
|
], |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2016 Vauxoo - https://www.vauxoo.com/ |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
|
from . import main |
@ -0,0 +1,44 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2016 Vauxoo - https://www.vauxoo.com/ |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
|
import pprint |
||||
|
|
||||
|
from openerp.addons.web import http |
||||
|
from openerp.http import request |
||||
|
from openerp import SUPERUSER_ID, exceptions |
||||
|
from openerp.tools.translate import _ |
||||
|
|
||||
|
|
||||
|
class WebhookController(http.Controller): |
||||
|
|
||||
|
@http.route(['/webhook/<webhook_name>'], type='json', |
||||
|
auth='none', method=['POST']) |
||||
|
def webhook(self, webhook_name, **post): |
||||
|
''' |
||||
|
:params string webhook_name: Name of webhook to use |
||||
|
Webhook odoo controller to receive json request and send to |
||||
|
driver method. |
||||
|
You will need create your webhook with http://0.0.0.0:0000/webhook |
||||
|
NOTE: Important use --db-filter params in odoo start. |
||||
|
''' |
||||
|
# Deprecated by webhook_name dynamic name |
||||
|
# webhook = webhook_registry.search_with_request( |
||||
|
# cr, SUPERUSER_ID, request, context=context) |
||||
|
webhook = request.env['webhook'].with_env( |
||||
|
request.env(user=SUPERUSER_ID)).search( |
||||
|
[('name', '=', webhook_name)], limit=1) |
||||
|
# TODO: Add security by secret string or/and ip consumer |
||||
|
if not webhook: |
||||
|
remote_addr = '' |
||||
|
if hasattr(request, 'httprequest'): |
||||
|
if hasattr(request.httprequest, 'remote_addr'): |
||||
|
remote_addr = request.httprequest.remote_addr |
||||
|
raise exceptions.ValidationError(_( |
||||
|
'webhook consumer [%s] from remote address [%s] ' |
||||
|
'not found jsonrequest [%s]' % ( |
||||
|
webhook_name, |
||||
|
remote_addr, |
||||
|
pprint.pformat(request.jsonrequest)[:450] |
||||
|
))) |
||||
|
webhook.run_webhook(request) |
@ -0,0 +1,16 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<!--webhook github data--> |
||||
|
<record model="webhook" id="webhook_github"> |
||||
|
<field name="name">github</field> |
||||
|
<field name="python_code_get_event">request.httprequest.headers.get('X-Github-Event')</field> |
||||
|
</record> |
||||
|
<record model="webhook.address" id="webhook_address_github"> |
||||
|
<field name="name">192.30.252.0/22</field> |
||||
|
<field name="webhook_id" ref="webhook_github"/> |
||||
|
</record> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,17 @@ |
|||||
|
<?xml version='1.0' encoding='UTF-8'?> |
||||
|
<openerp> |
||||
|
<data noupdate="1"> |
||||
|
|
||||
|
<record id="webhook_test" model="webhook"> |
||||
|
<field name="name">wehook_test</field> |
||||
|
<field name="python_code_get_event">request.httprequest.headers.get("X-Webhook-Test-Event")</field> |
||||
|
<field name="python_code_get_ip">request.httprequest.headers.get("X-Webhook-Test-Address")</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="webhook_address_localhost" model="webhook.address"> |
||||
|
<field name="name">127.0.0.1</field> |
||||
|
<field name="webhook_id" ref="webhook_test"/> |
||||
|
</record> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2016 Vauxoo - https://www.vauxoo.com/ |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
|
from . import webhook |
@ -0,0 +1,205 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2016 Vauxoo - https://www.vauxoo.com/ |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
|
import logging |
||||
|
import traceback |
||||
|
|
||||
|
from openerp import api, exceptions, fields, models, tools |
||||
|
from openerp.tools.safe_eval import safe_eval |
||||
|
from openerp.tools.translate import _ |
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
try: |
||||
|
import ipaddress |
||||
|
except ImportError as err: |
||||
|
_logger.debug(err) |
||||
|
|
||||
|
|
||||
|
class WebhookAddress(models.Model): |
||||
|
_name = 'webhook.address' |
||||
|
|
||||
|
name = fields.Char( |
||||
|
'IP or Network Address', |
||||
|
required=True, |
||||
|
help='IP or network address of your consumer webhook:\n' |
||||
|
'ip address e.g.: 10.10.0.8\n' |
||||
|
'network address e.g. of: 10.10.0.8/24', |
||||
|
) |
||||
|
webhook_id = fields.Many2one( |
||||
|
'webhook', 'Webhook', required=True, ondelete='cascade') |
||||
|
|
||||
|
|
||||
|
class Webhook(models.Model): |
||||
|
_name = 'webhook' |
||||
|
|
||||
|
name = fields.Char( |
||||
|
'Consumer name', |
||||
|
required=True, |
||||
|
help='Name of your consumer webhook. ' |
||||
|
'This name will be used in named of event methods') |
||||
|
address_ids = fields.One2many( |
||||
|
'webhook.address', 'webhook_id', 'IP or Network Address', |
||||
|
required=True, |
||||
|
help='This address will be filter to know who is ' |
||||
|
'consumer webhook') |
||||
|
python_code_get_event = fields.Text( |
||||
|
'Get event', |
||||
|
required=True, |
||||
|
help='Python code to get event from request data.\n' |
||||
|
'You have object.env.request variable with full ' |
||||
|
'webhook request.', |
||||
|
default='# You can use object.env.request variable ' |
||||
|
'to get full data of webhook request.\n' |
||||
|
'# Example:\n#request.httprequest.' |
||||
|
'headers.get("X-Github-Event")', |
||||
|
) |
||||
|
python_code_get_ip = fields.Text( |
||||
|
'Get IP', |
||||
|
required=True, |
||||
|
help='Python code to get remote IP address ' |
||||
|
'from request data.\n' |
||||
|
'You have object.env.request variable with full ' |
||||
|
'webhook request.', |
||||
|
default='# You can use object.env.request variable ' |
||||
|
'to get full data of webhook request.\n' |
||||
|
'# Example:\n' |
||||
|
'#object.env.request.httprequest.remote_addr' |
||||
|
'\nrequest.httprequest.remote_addr', |
||||
|
|
||||
|
) |
||||
|
active = fields.Boolean(default=True) |
||||
|
|
||||
|
@api.multi |
||||
|
def process_python_code(self, python_code, request=None): |
||||
|
""" |
||||
|
Execute a python code with eval. |
||||
|
:param string python_code: Python code to process |
||||
|
:param object request: Request object with data of json |
||||
|
and http request |
||||
|
:return: Result of process python code. |
||||
|
""" |
||||
|
self.ensure_one() |
||||
|
res = None |
||||
|
eval_dict = { |
||||
|
'user': self.env.user, |
||||
|
'object': self, |
||||
|
'request': request, |
||||
|
# copy context to prevent side-effects of eval |
||||
|
'context': dict(self.env.context), |
||||
|
} |
||||
|
try: |
||||
|
res = safe_eval(python_code, eval_dict) |
||||
|
except BaseException: |
||||
|
error = tools.ustr(traceback.format_exc()) |
||||
|
_logger.debug( |
||||
|
'python_code "%s" with dict [%s] error [%s]', |
||||
|
python_code, eval_dict, error) |
||||
|
if isinstance(res, basestring): |
||||
|
res = tools.ustr(res) |
||||
|
return res |
||||
|
|
||||
|
@api.model |
||||
|
def search_with_request(self, request): |
||||
|
""" |
||||
|
Method to search of all webhook |
||||
|
and return only one that match with remote address |
||||
|
and range of address. |
||||
|
:param object request: Request object with data of json |
||||
|
and http request |
||||
|
:return: object of webhook found or |
||||
|
if not found then return False |
||||
|
""" |
||||
|
for webhook in self.search([('active', '=', True)]): |
||||
|
remote_address = webhook.process_python_code( |
||||
|
webhook.python_code_get_ip, request) |
||||
|
if not remote_address: |
||||
|
continue |
||||
|
if webhook.is_address_range(remote_address): |
||||
|
return webhook |
||||
|
return False |
||||
|
|
||||
|
@api.multi |
||||
|
def is_address_range(self, remote_address): |
||||
|
""" |
||||
|
Check if a remote IP address is in range of one |
||||
|
IP or network address of current object data. |
||||
|
:param string remote_address: Remote IP address |
||||
|
:return: if remote address match then return True |
||||
|
else then return False |
||||
|
""" |
||||
|
self.ensure_one() |
||||
|
for address in self.address_ids: |
||||
|
ipn = ipaddress.ip_network(u'' + address.name) |
||||
|
hosts = [host.exploded for host in ipn.hosts()] |
||||
|
hosts.append(address.name) |
||||
|
if remote_address in hosts: |
||||
|
return True |
||||
|
return False |
||||
|
|
||||
|
@api.model |
||||
|
def get_event_methods(self, event_method_base): |
||||
|
""" |
||||
|
List all methods of current object that start with base name. |
||||
|
e.g. if exists methods called 'x1', 'x2' |
||||
|
and other ones called 'y1', 'y2' |
||||
|
if you have event_method_base='x' |
||||
|
Then will return ['x1', 'x2'] |
||||
|
:param string event_method_base: Name of method event base |
||||
|
returns: List of methods with that start wtih method base |
||||
|
""" |
||||
|
# TODO: Filter just callable attributes |
||||
|
return sorted( |
||||
|
attr for attr in dir(self) if attr.startswith( |
||||
|
event_method_base) |
||||
|
) |
||||
|
|
||||
|
@api.model |
||||
|
def get_ping_events(self): |
||||
|
""" |
||||
|
List all name of event type ping. |
||||
|
This event is a dummy event just to |
||||
|
know if a provider is working. |
||||
|
:return: List with names of ping events |
||||
|
""" |
||||
|
return ['ping'] |
||||
|
|
||||
|
@api.multi |
||||
|
def run_webhook(self, request): |
||||
|
""" |
||||
|
Run methods to process a webhook event. |
||||
|
Get all methods with base name |
||||
|
'run_CONSUMER_EVENT*' |
||||
|
Invoke all methods found. |
||||
|
:param object request: Request object with data of json |
||||
|
and http request |
||||
|
:return: True |
||||
|
""" |
||||
|
self.ensure_one() |
||||
|
event = self.process_python_code( |
||||
|
self.python_code_get_event, request) |
||||
|
if not event: |
||||
|
raise exceptions.ValidationError(_( |
||||
|
'event is not defined')) |
||||
|
method_event_name_base = \ |
||||
|
'run_' + self.name + \ |
||||
|
'_' + event |
||||
|
methods_event_name = self.get_event_methods(method_event_name_base) |
||||
|
if not methods_event_name: |
||||
|
# if is a 'ping' event then return True |
||||
|
# because the request is received fine. |
||||
|
if event in self.get_ping_events(): |
||||
|
return True |
||||
|
raise exceptions.ValidationError(_( |
||||
|
'Not defined methods "%s" yet' % ( |
||||
|
method_event_name_base))) |
||||
|
self.env.request = request |
||||
|
for method_event_name in methods_event_name: |
||||
|
method = getattr(self, method_event_name) |
||||
|
res_method = method() |
||||
|
if isinstance(res_method, list) and len(res_method) == 1: |
||||
|
if res_method[0] is NotImplemented: |
||||
|
_logger.debug( |
||||
|
'Not implemented method "%s" yet', method_event_name) |
||||
|
return True |
@ -0,0 +1,3 @@ |
|||||
|
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink |
||||
|
access_webhook_address,access_webhook_address,model_webhook_address,base.group_system,1,1,1,1 |
||||
|
access_webhook,access_webhook,model_webhook,base.group_system,1,1,1,1 |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2016 Vauxoo - https://www.vauxoo.com/ |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
|
from . import test_webhook_post |
@ -0,0 +1,124 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2016 Vauxoo - https://www.vauxoo.com/ |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
|
import json |
||||
|
import requests |
||||
|
|
||||
|
from openerp.tests.common import HttpCase |
||||
|
from openerp import api, exceptions, tools, models |
||||
|
from openerp.tools.translate import _ |
||||
|
|
||||
|
|
||||
|
HOST = '127.0.0.1' |
||||
|
PORT = tools.config['xmlrpc_port'] |
||||
|
|
||||
|
|
||||
|
class Webhook(models.Model): |
||||
|
_inherit = 'webhook' |
||||
|
|
||||
|
@api.multi |
||||
|
def run_wehook_test_get_foo(self): |
||||
|
""" |
||||
|
This method is just to test webhook. |
||||
|
This needs receive a json request with |
||||
|
next json values: {'foo': 'bar'} |
||||
|
If value is different will raise a error. |
||||
|
""" |
||||
|
self.ensure_one() |
||||
|
if self.env.request.jsonrequest['foo'] != 'bar': |
||||
|
raise exceptions.ValidationError(_("Wrong value received")) |
||||
|
|
||||
|
|
||||
|
class FakeHttpRequest(object): |
||||
|
remote_address = None |
||||
|
headers = {} |
||||
|
|
||||
|
|
||||
|
class FakeRequest(object): |
||||
|
def __init__(self, **args): |
||||
|
self.httprequest = FakeHttpRequest() |
||||
|
|
||||
|
|
||||
|
class TestWebhookPost(HttpCase): |
||||
|
def setUp(self): |
||||
|
super(TestWebhookPost, self).setUp() |
||||
|
self.webhook = self.env['webhook'] |
||||
|
self.url_base = "http://%s:%s" % (HOST, PORT) |
||||
|
self.url = self.get_webhook_url() |
||||
|
|
||||
|
def get_webhook_url(self, url='/webhook', |
||||
|
webhook_name="wehook_test"): |
||||
|
""" |
||||
|
:param string url: Full url of last url of webhook to use. |
||||
|
If you use a full url will return url |
||||
|
plus session_id |
||||
|
default: /webhook |
||||
|
:param string webhook_name: Name of webhook to process |
||||
|
default: webhook_test |
||||
|
:return: url with |
||||
|
http://IP:PORT/webhook/webhook_name?session_id=### |
||||
|
""" |
||||
|
webhook_name = webhook_name.replace('/', '') |
||||
|
if url.startswith('/'): |
||||
|
url = self.url_base + url + '/' + webhook_name |
||||
|
url += '?session_id=' + self.session_id |
||||
|
return url |
||||
|
|
||||
|
def post_webhook_event(self, event, url, data, remote_ip=None, |
||||
|
headers=None, params=None): |
||||
|
""" |
||||
|
:param string event String: Name of webhook event. |
||||
|
:param string url: Full url of webhook services. |
||||
|
:param dict data: Payload data of request. |
||||
|
:param string remote_ip: Remote IP of webhook to set in |
||||
|
test variable. |
||||
|
:param dict headers: Request headers with main data. |
||||
|
:param dict params: Extra values to send to webhook. |
||||
|
""" |
||||
|
if headers is None: |
||||
|
headers = {} |
||||
|
if remote_ip is None: |
||||
|
remote_ip = '127.0.0.1' |
||||
|
headers.update({ |
||||
|
'X-Webhook-Test-Event': event, |
||||
|
'X-Webhook-Test-Address': remote_ip, |
||||
|
}) |
||||
|
headers.setdefault('accept', 'application/json') |
||||
|
headers.setdefault('content-type', 'application/json') |
||||
|
payload = json.dumps(data) |
||||
|
response = requests.request( |
||||
|
"POST", url, data=payload, |
||||
|
headers=headers, params=params) |
||||
|
return response.json() |
||||
|
|
||||
|
def test_webhook_ping(self): |
||||
|
""" |
||||
|
Test to check that 'ping' generic method work fine! |
||||
|
'ping' event don't need to add it in inherit class. |
||||
|
""" |
||||
|
json_response = self.post_webhook_event( |
||||
|
'ping', self.url, {}) |
||||
|
has_error = json_response.get('error', False) |
||||
|
self.assertEqual( |
||||
|
has_error, False, 'Error in webhook ping test!') |
||||
|
|
||||
|
def test_webhook_get_foo(self): |
||||
|
""" |
||||
|
Test to check that 'get_foo' event from 'webhook_test' |
||||
|
work fine! |
||||
|
This event is defined in inherit method of test. |
||||
|
""" |
||||
|
json_response = self.post_webhook_event( |
||||
|
'get_foo', self.url, {'foo': 'bar'}) |
||||
|
self.assertEqual( |
||||
|
json_response.get('error', False), False, |
||||
|
'Error in webhook get foo test!.') |
||||
|
|
||||
|
def test_webhook_search_with_request(self): |
||||
|
"""Test to check that 'search_with_request' method works!""" |
||||
|
fake_req = FakeRequest() |
||||
|
fake_req.httprequest.headers['X-Webhook-Test-Address'] = '127.0.0.1' |
||||
|
wh = self.webhook.search_with_request(fake_req) |
||||
|
self.assertEqual(wh.id, self.env.ref('webhook.webhook_test').id, |
||||
|
"Search webhook from request IP info is not working") |
@ -0,0 +1,44 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<record id="view_webhook_form" model="ir.ui.view"> |
||||
|
<field name="name">view.webhook.form</field> |
||||
|
<field name="model">webhook</field> |
||||
|
<field name="priority" eval="20"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form> |
||||
|
<group> |
||||
|
<field name="name"/> |
||||
|
<field name="address_ids" widget="one2many_list"> |
||||
|
<tree string="Address" editable="top"> |
||||
|
<field name="name"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
<field name="python_code_get_event"/> |
||||
|
<field name="active"/> |
||||
|
</group> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
<record id="view_webhook_tree" model="ir.ui.view"> |
||||
|
<field name="name">view.webhook.tree</field> |
||||
|
<field name="model">webhook</field> |
||||
|
<field name="priority" eval="20"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree> |
||||
|
<field name="name"/> |
||||
|
<field name="active"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<record model="ir.actions.act_window" id="action_webhook"> |
||||
|
<field name="name">Webhook</field> |
||||
|
<field name="res_model">webhook</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
</record> |
||||
|
<menuitem id="webhook_menu_action" name="Webhook" parent="base.menu_automation" action="action_webhook"/> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue