Skip to content

Commit f48c937

Browse files
feat: rewrite notification using requests lib (#2098)
* feat: add make_api_call function for making authenticated API requests * refactor(notification): replace FrappeClient with make_api_call for API requests in notification handling * fix(notification): correct token sync error handling in raven_cloud_notifications.py * chore: bump version to 2.8.10
1 parent 47a8945 commit f48c937

9 files changed

Lines changed: 93 additions & 78 deletions

File tree

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "raven-web",
33
"private": true,
44
"license": "AGPL-3.0-only",
5-
"version": "2.8.9",
5+
"version": "2.8.10",
66
"type": "module",
77
"scripts": {
88
"dev": "vite",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "raven",
4-
"version": "2.8.9",
4+
"version": "2.8.10",
55
"description": "Messaging Application",
66
"workspaces": [
77
"frontend",

raven/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "2.8.9"
1+
__version__ = "2.8.10"
22

33
from raven.raven_integrations.doctype.raven_incoming_webhook.raven_incoming_webhook import ( # noqa
44
handle_incoming_webhook as webhook,

raven/api/notification.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,26 @@ def register_site_on_raven_cloud() -> None:
2929
"""
3030
Register the site on Raven Cloud
3131
"""
32+
from raven.utils import make_api_call
33+
3234
frappe.only_for("System Manager")
3335
raven_settings = frappe.get_single("Raven Settings")
3436

3537
if raven_settings.push_notification_service == "Raven":
3638

37-
client = FrappeClient(
38-
url=raven_settings.push_notification_server_url,
39+
url = f"{raven_settings.push_notification_server_url}/api/method/raven_cloud.api.notification.register_site"
40+
41+
response = make_api_call(
42+
url=url,
3943
api_key=raven_settings.push_notification_api_key,
4044
api_secret=raven_settings.get_password("push_notification_api_secret"),
41-
)
42-
43-
response = client.post_api(
44-
"raven_cloud.api.notification.register_site",
45+
method="POST",
4546
params={"site_name": urlparse(frappe.utils.get_url()).hostname},
4647
)
48+
message = response.get("message")
4749

48-
raven_settings.config = response.get("config")
49-
raven_settings.vapid_public_key = response.get("vapid_public_key")
50+
raven_settings.config = message.get("config")
51+
raven_settings.vapid_public_key = message.get("vapid_public_key")
5052
raven_settings.save()
5153
else:
5254
frappe.throw(_("Push notification service is not set to Raven Cloud."))

raven/notification.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from frappe.utils import get_datetime, get_system_timezone
77
from pytz import timezone, utc
88

9-
from raven.utils import get_channel_members
9+
from raven.raven_cloud_notifications import get_site_name
10+
from raven.utils import get_channel_members, make_api_call
1011

1112
MAX_NOTIFICATION_CONTENT_LENGTH = 1000
1213

@@ -208,27 +209,21 @@ def make_post_call_for_notification(messages, raven_settings):
208209
"""
209210
Make a post call to the push notification server to send the notification
210211
"""
211-
from base64 import b64encode
212-
213-
import requests
214-
215212
# instead of using the frappe client, we will use the requests library to make the post call
216213
# reason: FrappeClient's post_api method sends data in params which is not ideal for large payloads(Proxy returns JSON Decode errors as the URL is too long)
217214
# and post_request method uses "cmd" based key in it's payload which is weird semantically
218215

219216
api_key = raven_settings.push_notification_api_key
220217
api_secret = raven_settings.get_password("push_notification_api_secret")
221218

222-
token = b64encode(f"{api_key}:{api_secret}".encode()).decode()
223-
auth_header = {"Authorization": f"Basic {token}"}
219+
url = f"{raven_settings.push_notification_server_url}/api/method/raven_cloud.api.notification.send_to_users"
224220

225-
requests.post(
226-
f"{raven_settings.push_notification_server_url}/api/method/raven_cloud.api.notification.send_to_users",
227-
json={
228-
"messages": json.dumps(messages),
229-
"site_name": urlparse(frappe.utils.get_url()).hostname,
230-
},
231-
headers=auth_header,
221+
make_api_call(
222+
url=url,
223+
api_key=api_key,
224+
api_secret=api_secret,
225+
method="POST",
226+
params={"messages": json.dumps(messages), "site_name": get_site_name()},
232227
)
233228

234229

raven/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "raven-app",
3-
"version": "2.8.9",
3+
"version": "2.8.10",
44
"description": "",
55
"main": "index.js",
66
"scripts": {

raven/raven_cloud_notifications.py

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
from urllib.parse import urlparse
33

44
import frappe
5-
from frappe.frappeclient import FrappeClient
5+
6+
from raven.utils import make_api_call
67

78
# ----- Utility Functions -----
89

@@ -20,14 +21,13 @@ def add_token_to_raven_cloud(user_id, token):
2021
"""
2122
raven_settings = frappe.get_single("Raven Settings")
2223

23-
client = FrappeClient(
24-
url=raven_settings.push_notification_server_url,
24+
url = f"{raven_settings.push_notification_server_url}/api/method/raven_cloud.api.notification.create_user_token"
25+
26+
make_api_call(
27+
url=url,
2528
api_key=raven_settings.push_notification_api_key,
2629
api_secret=raven_settings.get_password("push_notification_api_secret"),
27-
)
28-
29-
client.post_api(
30-
"raven_cloud.api.notification.create_user_token",
30+
method="POST",
3131
params={
3232
"user_id": user_id,
3333
"token": token,
@@ -42,14 +42,13 @@ def delete_token_from_raven_cloud(user_id, token):
4242
"""
4343
raven_settings = frappe.get_single("Raven Settings")
4444

45-
client = FrappeClient(
46-
url=raven_settings.push_notification_server_url,
45+
url = f"{raven_settings.push_notification_server_url}/api/method/raven_cloud.api.notification.delete_user_token"
46+
47+
make_api_call(
48+
url=url,
4749
api_key=raven_settings.push_notification_api_key,
4850
api_secret=raven_settings.get_password("push_notification_api_secret"),
49-
)
50-
51-
client.post_api(
52-
"raven_cloud.api.notification.delete_user_token",
51+
method="POST",
5352
params={
5453
"user_id": user_id,
5554
"token": token,
@@ -65,33 +64,22 @@ def sync_users_tokens_to_raven_cloud():
6564
raven_settings = frappe.get_single("Raven Settings")
6665
tokens = frappe.db.get_all("Raven Push Token", fields=["user", "fcm_token"], order_by="user")
6766

68-
# Group tokens by user to ensure all tokens for a user are in the same chunk
69-
user_tokens = {}
70-
for token in tokens:
71-
user_tokens.setdefault(token["user"], []).append(token)
72-
73-
# Build chunks where all tokens for a user stay together
74-
chunks = []
75-
current_chunk = []
76-
for user, user_token_list in user_tokens.items():
77-
if len(current_chunk) + len(user_token_list) > 10 and current_chunk:
78-
chunks.append(current_chunk)
79-
current_chunk = []
80-
current_chunk.extend(user_token_list)
81-
if current_chunk:
82-
chunks.append(current_chunk)
83-
84-
for chunk in chunks:
85-
client = FrappeClient(
86-
url=raven_settings.push_notification_server_url,
87-
api_key=raven_settings.push_notification_api_key,
88-
api_secret=raven_settings.get_password("push_notification_api_secret"),
89-
)
67+
url = f"{raven_settings.push_notification_server_url}/api/method/raven_cloud.api.notification.import_user_tokens"
9068

91-
client.post_api(
92-
"raven_cloud.api.notification.import_user_tokens",
93-
params={
94-
"site_name": get_site_name(),
95-
"tokens": json.dumps(chunk),
96-
},
69+
response = make_api_call(
70+
url=url,
71+
api_key=raven_settings.push_notification_api_key,
72+
api_secret=raven_settings.get_password("push_notification_api_secret"),
73+
method="POST",
74+
params={"site_name": get_site_name(), "tokens": json.dumps(tokens)},
75+
)
76+
message = response.get("message")
77+
78+
if message.get("status") == "success":
79+
return "Tokens synced successfully"
80+
else:
81+
frappe.log_error(
82+
title="Raven Cloud Sync Tokens Error",
83+
message=f"Failed to sync tokens: {message.get('message')}",
9784
)
85+
return "Failed to sync tokens"

raven/scheduler/daily.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,35 @@
88
def sync_invalid_tokens():
99
"""
1010
This function is used to fetch the invalid tokens from Raven Cloud and sync them to the database.
11-
It runs a loop to fetch the Invalid tokens in batches of 10 from Raven Cloud and deletes them from the database.
11+
It runs a loop to fetch the Invalid tokens in batches of 100 from Raven Cloud and deletes them from the database.
1212
If has_more is true, it will continue to fetch the next batch of tokens.
1313
If has_more is false, it will break the loop. If there are no invalid tokens or error in fetching the invalid tokens, it will break the loop.
1414
"""
15+
from raven.utils import make_api_call
16+
1517
raven_settings = frappe.get_single("Raven Settings")
1618

17-
client = FrappeClient(
18-
url=raven_settings.push_notification_server_url,
19-
api_key=raven_settings.push_notification_api_key,
20-
api_secret=raven_settings.get_password("push_notification_api_secret"),
21-
)
19+
url = f"{raven_settings.push_notification_server_url}/api/method/raven_cloud.api.notification.sync_invalid_tokens"
2220

23-
batch_size = 10
21+
batch_size = 100
2422

2523
site_name = get_site_name()
2624

2725
while True:
2826
try:
29-
response = client.post_api(
30-
"raven_cloud.api.notification.sync_invalid_tokens",
27+
response = make_api_call(
28+
url=url,
29+
api_key=raven_settings.push_notification_api_key,
30+
api_secret=raven_settings.get_password("push_notification_api_secret"),
31+
method="POST",
3132
params={
3233
"site_name": site_name,
3334
"batch_size": batch_size,
3435
},
3536
)
37+
message = response.get("message")
3638

37-
invalid_tokens = response.get("invalid_tokens", [])
39+
invalid_tokens = message.get("invalid_tokens", [])
3840

3941
if not invalid_tokens:
4042
break
@@ -48,7 +50,7 @@ def sync_invalid_tokens():
4850
)
4951

5052
# Check if more tokens exist
51-
has_more = response.get("has_more", False)
53+
has_more = message.get("has_more", False)
5254
if not has_more:
5355
break
5456

raven/utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,31 @@ def clear_thread_reply_count_cache(thread_id: str):
224224
Clear the thread reply count cache
225225
"""
226226
frappe.cache().hdel("raven:thread_reply_count", thread_id)
227+
228+
229+
def make_api_call(url: str, api_key: str, api_secret: str, method: str, params: dict = None):
230+
"""
231+
Make an API call to the given URL
232+
"""
233+
from base64 import b64encode
234+
235+
import requests
236+
237+
token = b64encode(f"{api_key}:{api_secret}".encode()).decode()
238+
auth_header = {"Authorization": f"Basic {token}"}
239+
240+
if method == "GET":
241+
response = requests.get(url, headers=auth_header, params=params)
242+
elif method == "POST":
243+
response = requests.post(url, headers=auth_header, json=params)
244+
245+
# return response.json only if the response is successful
246+
if not response.ok:
247+
frappe.log_error(
248+
title="Raven Cloud API Error",
249+
message=f"Failed to make API call to {url}: {response.status_code} {response.text}",
250+
)
251+
raise Exception(f"Failed to make API call to {url}: {response.status_code} {response.text}")
252+
253+
# Return the JSON response
254+
return response.json()

0 commit comments

Comments
 (0)