File: //home/arjun/projects/buyercall_forms/buyercall/buyercall/blueprints/api2/doc/endpoints/webhooks.py
import logging
import time
from flask import (
Blueprint,
make_response,
request,
jsonify)
from datetime import datetime
from flask import Blueprint, jsonify, make_response
from buyercall.lib.util_rest import rest_partnership_account, rest_partnership, requires_auth, rest_is_partnership_account, rest_is_partnership
from flask_restx import Resource
from buyercall.blueprints.api2.doc import serializers
from buyercall.blueprints.api2.restplus import api
from buyercall.blueprints.partnership.models import Partnership, PartnershipAccount
from buyercall.blueprints.webhooks.models import Webhook
from sqlalchemy import func, or_, and_, extract
log = logging.getLogger(__name__)
ns = api.namespace('Webhooks', description='Operations related to webhooks.', path='/accounts')
@ns.route('/webhooks')
@api.doc(responses={200: 'OK',
400: 'Error performing operation.',
401: 'Unauthorized request.'})
class ApiWebhooksCollection(Resource):
@requires_auth
def get(self):
"""
Retrieve all the webhook call-back urls for a specific partnership.
<p>
The Webhook API GET endpoint should be used to retrieve the webhook call-back urls for a specific partnership.
Webhook call-back urls are not defined at a partnership account level, but rather at a partnership level. The
call-back information returned by the webhook url will contain partnership account specific information.
</p>
<br />
<p>
There are no required parameters. A response will
be returned, similar to the example below, based
on a successful request:
<br />
<br />
</p>
<pre class="code-background" style="color: white">
{
"created_on": "2019-07-16 00:30:00",
"deactivated_on": "",
"id": 6,
"is_deactivated": false,
"updated_on": "2019-07-16 01:06:55",
"webhook_url": "http://buyercall.pythonanywhere.com/local_wehook"
}
</pre>
"""
if rest_is_partnership and rest_partnership is not None:
webhook = Webhook.query\
.filter(and_(Webhook.partnership_id == rest_partnership.id, Webhook.is_deactivated == False))\
.first()
if webhook is not None:
result_deactivated_on = ''
if webhook.is_deactivated:
if webhook.deactivated_on is not None:
result_deactivated_on = webhook.deactivated_on.strftime('%Y-%m-%d %H:%M:%S')
return jsonify(
id=webhook.id,
webhook_url=webhook.webhook_url,
security_token=webhook.security_token,
is_deactivated=webhook.is_deactivated,
deactivated_on=result_deactivated_on,
created_on=webhook.date_created,
updated_on=webhook.date_updated
)
else:
api.abort(code=404, message="Error retrieving webhook. No webhook found.")
else:
api.abort(code=401)
@api.expect(serializers.webhook_add, validate=True)
@api.response(200, 'Webhook successfully created.')
@requires_auth
def post(self):
"""
Creates a webhook for a partnership account.
<p>
The Webhook API POST endpoint should be used to to receive a call-back response whenever a phone call occurs or
a text message is sent or received. For example, when a phone call is started the webhook call-back url will be called
sending the specific call start data as body response. A webhook url will be triggered based on specific event
types. Call or text message data responses will be sent via a webhook POST request based on the following event
types:
</p>
<br />
<p><b>operational_start_call</b> - Receive call data when a new inbound or outbound operational phone number call
starts.</p>
<p><b>operational_agent_call</b> - Receive call data when an agent answers an operational phone number call.</p>
<p><b>operational_end_call</b> - Receive call data when an operational phone number call ends.</p>
<p><b>mobile_start_call</b> - Receive call data when a new inbound or outbound mobile phone number call starts.</p>
<p><b>mobile_end_call</b> - Receive call data when a mobile phone number call ends.</p>
<p><b>operational_send_message</b> - Receive data when a operational phone number text message is sent.</p>
<p><b>operational_receive_message</b> - Receive data when a operational phone number text message is received.</p>
<p><b>mobile_send_message</b> - Receive data when a mobile phone number text message is sent.</p>
<p><b>mobile_receive_message</b> - Receive data when a mobile phone number text message is received.</p>
<br />
<p>
A webhook call-back url is defined at a partnership level. Therefore, only one webhook call-back url is required for
all partnership accounts. The call-back response data will not only include the call or message data, but also
contain a the partnership account id as well as the event type.
</p>
<br />
<p>
You require a valid webhook url to
make a successful request. A response will
be returned, similar to the example below, based
on a successful request. Please note a phone call response will look different to a message response.
<br />
<br />
</p>
<p>
Text Message Response:
</p>
<br />
<pre class="code-background" style="color: white">
{
"status": "received",
"direction": u'inbound',
"phone_number_id": 204,
"event_type": "mobile_receive_message",
"delivery_description": None,
"body_text": "Mooi man",
"id": 275,
"delivery_code": None,
"delivery_type": None,
"created_on": "2019-07-16 17:37:53",
"agent_id': 3,
"updated_on": "2019-07-16 17:37:53",
"from_": "+17739699892",
"type': "phone",
"partnership_account_id": 1,
"to": "+18722217507",
"media_url": ""
}
</pre>
<br />
<p>
Phone Call Response:
</p>
<br />
<pre class="code-background" style="color: white">
{
"status": "ringing",
"call_type": "outbound",
"widget_guid": None,
"progress_status": "new lead",
"call_source": "Phone number for app user CoCoButter",
"phonenumber_id": 204,
"caller_id": "CHICAGO, IL",
"id": 750,
"created_on": "2019-07-16 19:09:23",
"phonenumber": "+17739699892",
"agent_id": None,
"starttime": "2019-07-16 19:09:23",
"my_phone": "+18722217507",
"duration": None,
"updated_on": "2019-07-16 19:09:23",
"recording_url": "",
"endtime": None,
"type": "phone",
"partnership_account_id": 1,
"call_count": 0,
"event_type": "mobile_start_call"
}
</pre>
"""
if rest_is_partnership and rest_partnership is not None:
received = request.json
result_webhook_url = received['webhook_url']
result_token = ''
if 'security_token' in received:
result_token = received['security_token']
result = Webhook.create(result_webhook_url, result_token, rest_partnership.id, False)
if result > 0:
return True, 204
elif result == -2:
return api.abort(code=400, message="Error creating webhook. Webhook for partnership already exists.")
else:
return api.abort(code=404, message="Error creating webhook.")
else:
api.abort(code=401)
@ns.route('/webhooks/<int:whid>')
@api.doc(responses={200: 'OK',
400: 'Error performing operation.',
401: 'Unauthorized request.'},
params={'whid': 'The webhook ID.'})
class ApiWebhooksSpecific(Resource):
@api.expect(serializers.webhook_edit, validate=True)
@api.response(200, 'Webhook successfully updated.')
@requires_auth
def put(self, whid):
"""
Updates a webhook of a partnership account.
<p>The Webhook API PUT endpoint can be used to update a webhook call-back url for a specific partnership.</p>
<br />
<p>
You require a valid partnership webhook id to
make a successful request. A response code will be returned
on a successful request.
</p>
"""
if rest_is_partnership and rest_partnership is not None:
webhook = Webhook.query\
.filter(and_(Webhook.partnership_id == rest_partnership.id, Webhook.id == whid))\
.first()
if webhook is not None:
received = request.json
result_webhook_url = None
result_token = None
if 'webhook_url' in received:
result_webhook_url = received['webhook_url']
if 'security_token' in received:
result_token = received['security_token']
result = Webhook.update(whid, rest_partnership.id, result_webhook_url, result_token)
if result:
return True, 204
else:
return api.abort(code=404, message="Error updating webhook.")
else:
api.abort(code=404, message="Error updating webhook. Webhook not found.")
else:
api.abort(code=401)
@api.response(200, 'Webhook successfully deleted.')
@requires_auth
def delete(self, whid):
"""
Removes a webhook of a partnership account.
<p>The Webhook API DELETE endpoint can be used to deactive a webhook call-back url for a specific partnership.</p>
<br />
<p>
You require a valid partnership webhook id to
make a successful request. A response code will be returned
on a successful request.
</p>
"""
if rest_is_partnership and rest_partnership is not None:
webhook = Webhook.query\
.filter(and_(Webhook.partnership_id == rest_partnership.id, Webhook.id == whid))\
.first()
if webhook is not None:
result = Webhook.deactivate(whid, rest_partnership.id)
if result:
return True, 204
else:
return api.abort(code=400, message="Error deleting webhook.")
else:
api.abort(code=404, message="Error deleting webhook. Webhook not found.")
else:
api.abort(code=401)