Skip to content

Commit 6df0500

Browse files
authored
Merge pull request #78 from livechat/API-10478-webhooks-support
API-10478: Webhooks support
2 parents d769a86 + df17795 commit 6df0500

File tree

15 files changed

+7079
-1
lines changed

15 files changed

+7079
-1
lines changed

changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Change Log
22
All notable changes to this project will be documented in this file.
33

4+
## [0.3.2] - TBA
5+
6+
### Added
7+
- Webhooks support. Allows to easily convert webhook's body into parsed data classes.
8+
49
## [0.3.1] - 2022-05-26
510

611
### Changed

docs/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ <h1>Under construction...</h1>
88
<a href="customer_web/index.html">Customer WEB API</a>
99
<a href="configuration/index.html">Configuration API</a>
1010
<a href="reports/index.html">Reports API</a>
11+
<a href="webhooks/index.html">Webhooks</a>
1112
</body>
1213
</html>

docs/webhooks/index.html

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
6+
<meta name="generator" content="pdoc 0.9.2" />
7+
<title>webhooks API documentation</title>
8+
<meta name="description" content="" />
9+
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
10+
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
11+
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
12+
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
13+
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
14+
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
15+
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
16+
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
17+
</head>
18+
<body>
19+
<main>
20+
<article id="content">
21+
<header>
22+
<h1 class="title">Package <code>webhooks</code></h1>
23+
</header>
24+
<section id="section-intro">
25+
</section>
26+
<section>
27+
<h2 class="section-title" id="header-submodules">Sub-modules</h2>
28+
<dl>
29+
<dt><code class="name"><a title="webhooks.parser" href="parser.html">webhooks.parser</a></code></dt>
30+
<dd>
31+
<div class="desc"><p>Webhooks parser module.</p></div>
32+
</dd>
33+
<dt><code class="name"><a title="webhooks.v33" href="v33.html">webhooks.v33</a></code></dt>
34+
<dd>
35+
<div class="desc"><p>API v3.3 webhooks data classes.</p></div>
36+
</dd>
37+
<dt><code class="name"><a title="webhooks.v34" href="v34.html">webhooks.v34</a></code></dt>
38+
<dd>
39+
<div class="desc"><p>API v3.4 webhooks data classes.</p></div>
40+
</dd>
41+
<dt><code class="name"><a title="webhooks.v35" href="v35.html">webhooks.v35</a></code></dt>
42+
<dd>
43+
<div class="desc"><p>API v3.5 webhooks data classes.</p></div>
44+
</dd>
45+
</dl>
46+
</section>
47+
<section>
48+
</section>
49+
<section>
50+
</section>
51+
<section>
52+
</section>
53+
</article>
54+
<nav id="sidebar">
55+
<h1>Index</h1>
56+
<div class="toc">
57+
<ul></ul>
58+
</div>
59+
<ul id="index">
60+
<li><h3><a href="#header-submodules">Sub-modules</a></h3>
61+
<ul>
62+
<li><code><a title="webhooks.parser" href="parser.html">webhooks.parser</a></code></li>
63+
<li><code><a title="webhooks.v33" href="v33.html">webhooks.v33</a></code></li>
64+
<li><code><a title="webhooks.v34" href="v34.html">webhooks.v34</a></code></li>
65+
<li><code><a title="webhooks.v35" href="v35.html">webhooks.v35</a></code></li>
66+
</ul>
67+
</li>
68+
</ul>
69+
</nav>
70+
</main>
71+
<footer id="footer">
72+
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.2</a>.</p>
73+
</footer>
74+
</body>
75+
</html>

docs/webhooks/parser.html

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
6+
<meta name="generator" content="pdoc 0.9.2" />
7+
<title>webhooks.parser API documentation</title>
8+
<meta name="description" content="Webhooks parser module." />
9+
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
10+
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
11+
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
12+
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
13+
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
14+
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
15+
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
16+
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
17+
</head>
18+
<body>
19+
<main>
20+
<article id="content">
21+
<header>
22+
<h1 class="title">Module <code>webhooks.parser</code></h1>
23+
</header>
24+
<section id="section-intro">
25+
<p>Webhooks parser module.</p>
26+
<details class="source">
27+
<summary>
28+
<span>Expand source code</span>
29+
</summary>
30+
<pre><code class="python">&#39;&#39;&#39; Webhooks parser module. &#39;&#39;&#39;
31+
32+
from typing import Union
33+
34+
from livechat.config import CONFIG
35+
from livechat.webhooks.v33 import WebhookV33
36+
from livechat.webhooks.v34 import WebhookV34
37+
from livechat.webhooks.v35 import WebhookV35
38+
39+
stable_version = CONFIG.get(&#39;stable&#39;)
40+
41+
42+
def parse_webhook(
43+
wh_body: dict,
44+
version: str = stable_version,
45+
) -&gt; Union[WebhookV33, WebhookV34, WebhookV35]:
46+
&#39;&#39;&#39; Parses provided `wh_body` to a `Webhook` data class.
47+
48+
Args:
49+
wh_body (dict): Webhook body received from LiveChat API.
50+
version (str): API&#39;s version. Defaults to the stable version of API.
51+
52+
Returns:
53+
Webhook: data class with fields parsed from `wh_body`.
54+
55+
Raises:
56+
ValueError: If provided `wh_body` is invalid (contains additional,
57+
invalid or missing fields).
58+
&#39;&#39;&#39;
59+
webhook_data_class = {
60+
&#39;3.3&#39;: WebhookV33,
61+
&#39;3.4&#39;: WebhookV34,
62+
&#39;3.5&#39;: WebhookV35,
63+
}.get(version)
64+
try:
65+
parsed_wh = webhook_data_class(**wh_body)
66+
except TypeError as error:
67+
raise ValueError(
68+
&#39;Invalid webhook body. It should contain the following fields: &#39;
69+
f&#39;{webhook_data_class.__annotations__}&#39;) from error
70+
try:
71+
parsed_wh.payload = parsed_wh.payload_data_class()(**parsed_wh.payload)
72+
except KeyError as error:
73+
raise ValueError(
74+
f&#39;`{parsed_wh.action}` is invalid webhook action. &#39;
75+
&#39;Check the correctness of the webhook body provided.&#39;) from error
76+
except TypeError as error:
77+
raise ValueError(
78+
&#39;Invalid webhook payload. It should contain the following fields: &#39;
79+
f&#39;{parsed_wh.payload_data_class().__annotations__}&#39;) from error
80+
return parsed_wh</code></pre>
81+
</details>
82+
</section>
83+
<section>
84+
</section>
85+
<section>
86+
</section>
87+
<section>
88+
<h2 class="section-title" id="header-functions">Functions</h2>
89+
<dl>
90+
<dt id="webhooks.parser.parse_webhook"><code class="name flex">
91+
<span>def <span class="ident">parse_webhook</span></span>(<span>wh_body: dict, version: str = '3.4') ‑> Union[livechat.webhooks.v33.WebhookV33, livechat.webhooks.v34.WebhookV34, livechat.webhooks.v35.WebhookV35]</span>
92+
</code></dt>
93+
<dd>
94+
<div class="desc"><p>Parses provided <code>wh_body</code> to a <code>Webhook</code> data class.</p>
95+
<h2 id="args">Args</h2>
96+
<dl>
97+
<dt><strong><code>wh_body</code></strong> :&ensp;<code>dict</code></dt>
98+
<dd>Webhook body received from LiveChat API.</dd>
99+
<dt><strong><code>version</code></strong> :&ensp;<code>str</code></dt>
100+
<dd>API's version. Defaults to the stable version of API.</dd>
101+
</dl>
102+
<h2 id="returns">Returns</h2>
103+
<dl>
104+
<dt><code>Webhook</code></dt>
105+
<dd>data class with fields parsed from <code>wh_body</code>.</dd>
106+
</dl>
107+
<h2 id="raises">Raises</h2>
108+
<dl>
109+
<dt><code>ValueError</code></dt>
110+
<dd>If provided <code>wh_body</code> is invalid (contains additional,
111+
invalid or missing fields).</dd>
112+
</dl></div>
113+
<details class="source">
114+
<summary>
115+
<span>Expand source code</span>
116+
</summary>
117+
<pre><code class="python">def parse_webhook(
118+
wh_body: dict,
119+
version: str = stable_version,
120+
) -&gt; Union[WebhookV33, WebhookV34, WebhookV35]:
121+
&#39;&#39;&#39; Parses provided `wh_body` to a `Webhook` data class.
122+
123+
Args:
124+
wh_body (dict): Webhook body received from LiveChat API.
125+
version (str): API&#39;s version. Defaults to the stable version of API.
126+
127+
Returns:
128+
Webhook: data class with fields parsed from `wh_body`.
129+
130+
Raises:
131+
ValueError: If provided `wh_body` is invalid (contains additional,
132+
invalid or missing fields).
133+
&#39;&#39;&#39;
134+
webhook_data_class = {
135+
&#39;3.3&#39;: WebhookV33,
136+
&#39;3.4&#39;: WebhookV34,
137+
&#39;3.5&#39;: WebhookV35,
138+
}.get(version)
139+
try:
140+
parsed_wh = webhook_data_class(**wh_body)
141+
except TypeError as error:
142+
raise ValueError(
143+
&#39;Invalid webhook body. It should contain the following fields: &#39;
144+
f&#39;{webhook_data_class.__annotations__}&#39;) from error
145+
try:
146+
parsed_wh.payload = parsed_wh.payload_data_class()(**parsed_wh.payload)
147+
except KeyError as error:
148+
raise ValueError(
149+
f&#39;`{parsed_wh.action}` is invalid webhook action. &#39;
150+
&#39;Check the correctness of the webhook body provided.&#39;) from error
151+
except TypeError as error:
152+
raise ValueError(
153+
&#39;Invalid webhook payload. It should contain the following fields: &#39;
154+
f&#39;{parsed_wh.payload_data_class().__annotations__}&#39;) from error
155+
return parsed_wh</code></pre>
156+
</details>
157+
</dd>
158+
</dl>
159+
</section>
160+
<section>
161+
</section>
162+
</article>
163+
<nav id="sidebar">
164+
<h1>Index</h1>
165+
<div class="toc">
166+
<ul></ul>
167+
</div>
168+
<ul id="index">
169+
<li><h3>Super-module</h3>
170+
<ul>
171+
<li><code><a title="webhooks" href="index.html">webhooks</a></code></li>
172+
</ul>
173+
</li>
174+
<li><h3><a href="#header-functions">Functions</a></h3>
175+
<ul class="">
176+
<li><code><a title="webhooks.parser.parse_webhook" href="#webhooks.parser.parse_webhook">parse_webhook</a></code></li>
177+
</ul>
178+
</li>
179+
</ul>
180+
</nav>
181+
</main>
182+
<footer id="footer">
183+
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.2</a>.</p>
184+
</footer>
185+
</body>
186+
</html>

0 commit comments

Comments
 (0)