Merge pull request #4090 from revant/web_hooks
Web hooks or Reverse API
This commit is contained in:
commit
fdebb727ed
16 changed files with 923 additions and 0 deletions
|
|
@ -72,6 +72,12 @@ def get_data():
|
|||
"name": "GSuite Templates",
|
||||
"description": _("Google GSuite Templates to integration with DocTypes"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Webhook",
|
||||
"description": _("Webhooks calling API requests into web apps"),
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
BIN
frappe/docs/assets/img/webhook.png
Normal file
BIN
frappe/docs/assets/img/webhook.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
103
frappe/docs/user/en/guides/integration/webhooks.md
Normal file
103
frappe/docs/user/en/guides/integration/webhooks.md
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Webhooks
|
||||
|
||||
Webhooks are "user-defined HTTP callbacks". You can create webhook which triggers on Doc Event of the selected DocType. When the `doc_events` occurs, the source site makes an HTTP request to the URI configured for the webhook. Users can configure them to cause events on one site to invoke behaviour on another.
|
||||
|
||||
#### Configure Webhook
|
||||
|
||||
To add Webhook go to
|
||||
|
||||
> Integrations > External Documents > Webhook
|
||||
|
||||
Webhook
|
||||
|
||||
<img class="screenshot" src="/docs/assets/img/webhook.png">
|
||||
|
||||
1. Select the DocType for which hook needs to be triggered e.g. Note
|
||||
2. Select the DocEvent for which hook needs to be triggered e.g. on_trash
|
||||
3. Enter a valid request URL. On occurence of DocEvent, POST request with doc's json as data is made to the URL.
|
||||
4. Optionally you can add headers to the request to be made. Useful for sending api key if required.
|
||||
5. Optionally you can select fields and set its `key` to be sent as data json
|
||||
|
||||
e.g. Webhook
|
||||
|
||||
- **DocType** : `Quotation`
|
||||
- **Doc Event** : `on_update`
|
||||
- **Request URL** : `https://httpbin.org/post`
|
||||
- **Webhook Data** :
|
||||
1. **Fieldname** : `name` and **Key** : `id`
|
||||
2. **Fieldname** : `items` and **Key** : `lineItems`
|
||||
|
||||
Note: if no headers or data is present, request will be made without any header or body
|
||||
|
||||
Example response of request sent by frappe server on `Quotation` - `on_update` to https://httpbin.org/post:
|
||||
|
||||
```
|
||||
{
|
||||
"args": {},
|
||||
"data": "{\"lineItems\": [{\"stock_qty\": 1.0, \"base_price_list_rate\": 1.0, \"image\": \"\", \"creation\": \"2017-09-14 13:41:58.373023\", \"base_amount\": 1.0, \"qty\": 1.0, \"margin_rate_or_amount\": 0.0, \"rate\": 1.0, \"owner\": \"Administrator\", \"stock_uom\": \"Unit\", \"base_net_amount\": 1.0, \"page_break\": 0, \"modified_by\": \"Administrator\", \"base_net_rate\": 1.0, \"discount_percentage\": 0.0, \"item_name\": \"I1\", \"amount\": 1.0, \"actual_qty\": 0.0, \"net_rate\": 1.0, \"conversion_factor\": 1.0, \"warehouse\": \"Finished Goods - R\", \"docstatus\": 0, \"prevdoc_docname\": null, \"uom\": \"Unit\", \"description\": \"I1\", \"parent\": \"QTN-00001\", \"brand\": null, \"gst_hsn_code\": null, \"base_rate\": 1.0, \"item_code\": \"I1\", \"projected_qty\": 0.0, \"margin_type\": \"\", \"doctype\": \"Quotation Item\", \"rate_with_margin\": 0.0, \"pricing_rule\": null, \"price_list_rate\": 1.0, \"name\": \"QUOD/00001\", \"idx\": 1, \"item_tax_rate\": \"{}\", \"item_group\": \"Products\", \"modified\": \"2017-09-14 17:09:51.239271\", \"parenttype\": \"Quotation\", \"customer_item_code\": null, \"net_amount\": 1.0, \"prevdoc_doctype\": null, \"parentfield\": \"items\"}], \"id\": \"QTN-00001\"}",
|
||||
"files": {},
|
||||
"form": {},
|
||||
"headers": {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate",
|
||||
"Connection": "close",
|
||||
"Content-Length": "1075",
|
||||
"Host": "httpbin.org",
|
||||
"User-Agent": "python-requests/2.18.1"
|
||||
},
|
||||
"json": {
|
||||
"id": "QTN-00001",
|
||||
"lineItems": [
|
||||
{
|
||||
"actual_qty": 0.0,
|
||||
"amount": 1.0,
|
||||
"base_amount": 1.0,
|
||||
"base_net_amount": 1.0,
|
||||
"base_net_rate": 1.0,
|
||||
"base_price_list_rate": 1.0,
|
||||
"base_rate": 1.0,
|
||||
"brand": null,
|
||||
"conversion_factor": 1.0,
|
||||
"creation": "2017-09-14 13:41:58.373023",
|
||||
"customer_item_code": null,
|
||||
"description": "I1",
|
||||
"discount_percentage": 0.0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Quotation Item",
|
||||
"gst_hsn_code": null,
|
||||
"idx": 1,
|
||||
"image": "",
|
||||
"item_code": "I1",
|
||||
"item_group": "Products",
|
||||
"item_name": "I1",
|
||||
"item_tax_rate": "{}",
|
||||
"margin_rate_or_amount": 0.0,
|
||||
"margin_type": "",
|
||||
"modified": "2017-09-14 17:09:51.239271",
|
||||
"modified_by": "Administrator",
|
||||
"name": "QUOD/00001",
|
||||
"net_amount": 1.0,
|
||||
"net_rate": 1.0,
|
||||
"owner": "Administrator",
|
||||
"page_break": 0,
|
||||
"parent": "QTN-00001",
|
||||
"parentfield": "items",
|
||||
"parenttype": "Quotation",
|
||||
"prevdoc_docname": null,
|
||||
"prevdoc_doctype": null,
|
||||
"price_list_rate": 1.0,
|
||||
"pricing_rule": null,
|
||||
"projected_qty": 0.0,
|
||||
"qty": 1.0,
|
||||
"rate": 1.0,
|
||||
"rate_with_margin": 0.0,
|
||||
"stock_qty": 1.0,
|
||||
"stock_uom": "Unit",
|
||||
"uom": "Unit",
|
||||
"warehouse": "Finished Goods - R"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": "https://httpbin.org/post"
|
||||
}
|
||||
```
|
||||
0
frappe/integrations/doctype/webhook/__init__.py
Normal file
0
frappe/integrations/doctype/webhook/__init__.py
Normal file
23
frappe/integrations/doctype/webhook/test_webhook.js
Normal file
23
frappe/integrations/doctype/webhook/test_webhook.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Webhook", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Webhook
|
||||
() => frappe.tests.make('Webhook', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
21
frappe/integrations/doctype/webhook/test_webhook.py
Normal file
21
frappe/integrations/doctype/webhook/test_webhook.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
class TestWebhook(unittest.TestCase):
|
||||
def test_validate_docevents(self):
|
||||
doc = frappe.new_doc("Webhook")
|
||||
doc.webhook_doctype = "User"
|
||||
doc.webhook_docevent = "on_submit"
|
||||
doc.request_url = "https://httpbin.org/post"
|
||||
self.assertRaises(frappe.ValidationError, doc.save)
|
||||
def test_validate_request_url(self):
|
||||
doc = frappe.new_doc("Webhook")
|
||||
doc.webhook_doctype = "User"
|
||||
doc.webhook_docevent = "after_insert"
|
||||
doc.request_url = "httpbin.org?post"
|
||||
self.assertRaises(frappe.ValidationError, doc.save)
|
||||
46
frappe/integrations/doctype/webhook/webhook.js
Normal file
46
frappe/integrations/doctype/webhook/webhook.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2017, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.webhook = {
|
||||
set_fieldname_select: function(frm) {
|
||||
var doc = frm.doc;
|
||||
if (doc.webhook_doctype) {
|
||||
frappe.model.with_doctype(doc.webhook_doctype, function() {
|
||||
var fields = $.map(frappe.get_doc("DocType", frm.doc.webhook_doctype).fields, function(d) {
|
||||
if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
|
||||
d.fieldtype === 'Table') {
|
||||
return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
|
||||
}
|
||||
else if (d.fieldtype === 'Currency' || d.fieldtype === 'Float') {
|
||||
return { label: d.label, value: d.fieldname };
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
fields.unshift({"label":"Name (Doc Name)","value":"name"});
|
||||
frappe.meta.get_docfield("Webhook Data", "fieldname", frm.doc.name).options = [""].concat(fields);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
frappe.ui.form.on('Webhook', {
|
||||
refresh: function(frm) {
|
||||
frappe.webhook.set_fieldname_select(frm);
|
||||
},
|
||||
webhook_doctype: function(frm) {
|
||||
frappe.webhook.set_fieldname_select(frm);
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Webhook Data", {
|
||||
fieldname: function(frm, doctype, name) {
|
||||
var doc = frappe.get_doc(doctype, name);
|
||||
var df = $.map(frappe.get_doc("DocType", frm.doc.webhook_doctype).fields, function(d) {
|
||||
return doc.fieldname == d.fieldname ? d : null;
|
||||
})[0];
|
||||
doc.key = df != undefined ? df.fieldname : "name";
|
||||
frm.refresh_field("webhook_data");
|
||||
}
|
||||
});
|
||||
367
frappe/integrations/doctype/webhook/webhook.json
Normal file
367
frappe/integrations/doctype/webhook/webhook.json
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2017-09-08 16:16:13.060641",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "sb_doc_events",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Doc Events",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "",
|
||||
"fieldname": "webhook_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "cb_doc_events",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "webhook_docevent",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Doc Event",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "after_insert\non_update\non_submit\non_cancel\non_trash\non_update_after_submit\non_change",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "sb_webhook",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Webhook Request",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "request_url",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Request URL",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "sb_webhook_headers",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Webhook Headers",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "webhook_headers",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Headers",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Webhook Header",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "sb_webhook_data",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Webhook Data",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "webhook_data",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Data",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Webhook Data",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-14 13:16:53.974340",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "Webhook",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
102
frappe/integrations/doctype/webhook/webhook.py
Normal file
102
frappe/integrations/doctype/webhook/webhook.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json, requests
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from six.moves.urllib.parse import urlparse
|
||||
from time import sleep
|
||||
|
||||
class Webhook(Document):
|
||||
def autoname(self):
|
||||
self.name = self.webhook_doctype + "-" + self.webhook_docevent
|
||||
def validate(self):
|
||||
self.validate_docevent()
|
||||
self.validate_request_url()
|
||||
self.validate_repeating_fields()
|
||||
def validate_docevent(self):
|
||||
if self.webhook_doctype:
|
||||
is_submittable = frappe.get_value("DocType", self.webhook_doctype, "is_submittable")
|
||||
if not is_submittable and self.webhook_docevent in ["on_submit", "on_cancel", "on_update_after_submit"]:
|
||||
frappe.throw(_("DocType must be Submittable for the selected Doc Event"))
|
||||
def validate_request_url(self):
|
||||
try:
|
||||
request_url = urlparse(self.request_url).netloc
|
||||
if not request_url:
|
||||
raise frappe.ValidationError
|
||||
except Exception as e:
|
||||
frappe.throw(_("Check Request URL"), exc=e)
|
||||
def validate_repeating_fields(self):
|
||||
"""Error when Same Field is entered multiple times in webhook_data"""
|
||||
webhook_data = []
|
||||
for entry in self.webhook_data:
|
||||
webhook_data.append(entry.fieldname)
|
||||
|
||||
if len(webhook_data)!= len(set(webhook_data)):
|
||||
frappe.throw(_("Same Field is entered more than once"))
|
||||
|
||||
def enqueue_webhook(doc, webhook):
|
||||
webhook = frappe.get_doc("Webhook", webhook.get("name"))
|
||||
headers = {}
|
||||
data = {}
|
||||
if webhook.webhook_headers:
|
||||
for h in webhook.webhook_headers:
|
||||
if h.get("key") and h.get("value"):
|
||||
headers[h.get("key")] = h.get("value")
|
||||
if webhook.webhook_data:
|
||||
for w in webhook.webhook_data:
|
||||
for k, v in doc.as_dict().items():
|
||||
if k == w.fieldname:
|
||||
data[w.key] = v
|
||||
for i in range(3):
|
||||
try:
|
||||
r = requests.post(webhook.request_url, data=json.dumps(data), headers=headers, timeout=5)
|
||||
r.raise_for_status()
|
||||
frappe.logger().debug({"webhook_success":r.text})
|
||||
break
|
||||
except Exception as e:
|
||||
frappe.logger().debug({"webhook_error":e, "try": i+1})
|
||||
sleep(3*i + 1)
|
||||
if i !=2:
|
||||
continue
|
||||
else:
|
||||
raise e
|
||||
|
||||
def run_webhooks(doc, method):
|
||||
'''Run webhooks for this method'''
|
||||
if frappe.flags.in_import or frappe.flags.in_patch or frappe.flags.in_install:
|
||||
return
|
||||
|
||||
if not getattr(frappe.local, 'webhooks_executed', None):
|
||||
frappe.local.webhooks_executed = []
|
||||
|
||||
if doc.flags.webhooks == None:
|
||||
webhooks = frappe.cache().hget('webhooks', doc.doctype)
|
||||
if webhooks==None:
|
||||
webhooks = frappe.get_all('Webhook',
|
||||
fields=["name", "webhook_docevent", "webhook_doctype"])
|
||||
frappe.cache().hset('webhooks', doc.doctype, webhooks)
|
||||
doc.flags.webhooks = webhooks
|
||||
|
||||
if not doc.flags.webhooks:
|
||||
return
|
||||
|
||||
def _webhook_request(webhook):
|
||||
if not webhook.name in frappe.local.webhooks_executed:
|
||||
frappe.enqueue("frappe.integrations.doctype.webhook.webhook.enqueue_webhook", doc=doc, webhook=webhook)
|
||||
frappe.local.webhooks_executed.append(webhook.name)
|
||||
|
||||
event_list = ["on_update", "after_insert", "on_submit", "on_cancel", "on_trash"]
|
||||
|
||||
if not doc.flags.in_insert:
|
||||
# value change is not applicable in insert
|
||||
event_list.append('on_change')
|
||||
event_list.append('before_update_after_submit')
|
||||
|
||||
for webhook in doc.flags.webhooks:
|
||||
event = method if method in event_list else None
|
||||
if event and webhook.webhook_docevent == event and webhook.webhook_doctype == doc.doctype:
|
||||
_webhook_request(webhook)
|
||||
0
frappe/integrations/doctype/webhook_data/__init__.py
Normal file
0
frappe/integrations/doctype/webhook_data/__init__.py
Normal file
130
frappe/integrations/doctype/webhook_data/webhook_data.json
Normal file
130
frappe/integrations/doctype/webhook_data/webhook_data.json
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2017-09-14 12:08:50.302810",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "fieldname",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fieldname",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "cb_doc_data",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "key",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Key",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-14 13:16:58.252176",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "Webhook Data",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
||||
10
frappe/integrations/doctype/webhook_data/webhook_data.py
Normal file
10
frappe/integrations/doctype/webhook_data/webhook_data.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class WebhookData(Document):
|
||||
pass
|
||||
0
frappe/integrations/doctype/webhook_header/__init__.py
Normal file
0
frappe/integrations/doctype/webhook_header/__init__.py
Normal file
101
frappe/integrations/doctype/webhook_header/webhook_header.json
Normal file
101
frappe/integrations/doctype/webhook_header/webhook_header.json
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2017-09-08 16:27:39.195379",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "key",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Key",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "value",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Value",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-08 16:28:20.025612",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "Webhook Header",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
10
frappe/integrations/doctype/webhook_header/webhook_header.py
Normal file
10
frappe/integrations/doctype/webhook_header/webhook_header.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class WebhookHeader(Document):
|
||||
pass
|
||||
|
|
@ -169,6 +169,7 @@ class Document(BaseDocument):
|
|||
return
|
||||
|
||||
self.flags.email_alerts_executed = []
|
||||
setattr(frappe.local, 'webhooks_executed', [])
|
||||
|
||||
if ignore_permissions!=None:
|
||||
self.flags.ignore_permissions = ignore_permissions
|
||||
|
|
@ -242,6 +243,7 @@ class Document(BaseDocument):
|
|||
return
|
||||
|
||||
self.flags.email_alerts_executed = []
|
||||
setattr(frappe.local, 'webhooks_executed', [])
|
||||
|
||||
if ignore_permissions!=None:
|
||||
self.flags.ignore_permissions = ignore_permissions
|
||||
|
|
@ -671,6 +673,8 @@ class Document(BaseDocument):
|
|||
out = Document.hook(fn)(self, *args, **kwargs)
|
||||
|
||||
self.run_email_alerts(method)
|
||||
from frappe.integrations.doctype.webhook.webhook import run_webhooks
|
||||
run_webhooks(self, method)
|
||||
|
||||
return out
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue