Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async rest client in Channels #1101

Closed

Conversation

nupur-khare
Copy link
Contributor

@nupur-khare nupur-khare commented Dec 28, 2023

  1. Added Async rest client in Channels (whatsapp, messenger, msteams) instead of requests.
  2. Fixed unit and integration test cases for the same.

Summary by CodeRabbit

  • New Features

    • Introduced a new MessengerClientOutput class for enhanced interaction with the Facebook Messenger API.
    • Implemented asynchronous API communication in WhatsApp clients for improved performance.
  • Improvements

    • Upgraded WhatsApp messaging methods to asynchronous operations.
    • Enhanced Microsoft Teams bot to handle asynchronous requests and token management.
  • Bug Fixes

    • Adjusted message broadcasting to use async execution in event definitions.
  • Tests

    • Updated integration and unit tests to support asynchronous operations and response simulations.
  • Documentation

    • Not mentioned in the provided changes summary.

… instead of requests.

2. Fixed unit and integration test cases for the same.
Copy link

coderabbitai bot commented Dec 28, 2023

Walkthrough

Walkthrough

The suite of updates predominantly transitions various components from synchronous to asynchronous operations, enhancing the system's efficiency and scalability. The affected areas include interaction with Facebook Messenger, WhatsApp, and Microsoft Teams through their respective APIs, as well as internal message broadcasting and testing frameworks. The move to async/await patterns and AioRestClient for HTTP requests signifies a shift towards modern, non-blocking I/O across the platform.

Changes

File Path Change Summary
.../messenger/messenger_client.py
.../messenger.py
Introduced MessengerClientOutput class for Facebook Messenger API interaction, and modified existing functions.
.../whatsapp/cloud.py
.../whatsapp/dialog360.py
.../whatsapp/on_premise.py
.../channels/broadcast/whatsapp.py
Transitioned to asynchronous operations using async/await and replaced requests with AioRestClient.
.../msteams.py Integrated AioRestClient for asynchronous requests and improved error handling and token management.
.../events/definitions/base.py
.../events/definitions/message_broadcast.py
Changed methods to asynchronous with async def and implemented await for message sending.
.../integration_test/chat_service_test.py
.../unit_test/channel_client_test.py
.../unit_test/events/events_test.py
Updated tests for asynchronous operations using pytest.mark.asyncio and aioresponses.

🐇✨📜✨🐇

In the land of code, the rabbits hopped,
To async fields, where blocking stopped.
With await and async in their kit,
They crafted code that's truly lit!
🌟🚀🌟

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • You can directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
  • You can tag CodeRabbit on specific lines of code or entire files in the PR by tagging @coderabbitai in a comment. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • You can tag @coderabbitai in a PR comment and ask questions about the PR and the codebase. Examples:
    • @coderabbitai generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid.
    • @coderabbitai read the files in the src/scheduler package and generate README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 40

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between ef69c62 and df9ba31.
Files selected for processing (12)
  • kairon/chat/handlers/channels/clients/messenger/messenger_client.py (1 hunks)
  • kairon/chat/handlers/channels/clients/whatsapp/cloud.py (7 hunks)
  • kairon/chat/handlers/channels/clients/whatsapp/dialog360.py (2 hunks)
  • kairon/chat/handlers/channels/clients/whatsapp/on_premise.py (3 hunks)
  • kairon/chat/handlers/channels/messenger.py (4 hunks)
  • kairon/chat/handlers/channels/msteams.py (5 hunks)
  • kairon/events/definitions/base.py (1 hunks)
  • kairon/events/definitions/message_broadcast.py (2 hunks)
  • kairon/shared/channels/broadcast/whatsapp.py (6 hunks)
  • tests/integration_test/chat_service_test.py (10 hunks)
  • tests/unit_test/channel_client_test.py (11 hunks)
  • tests/unit_test/events/events_test.py (19 hunks)
Additional comments: 34
kairon/events/definitions/base.py (1)
  • 17-18: The execute method has been correctly updated to an asynchronous definition. Ensure that all calls to this method are updated to use await and that the event loop is properly managed where this method is invoked.
kairon/chat/handlers/channels/clients/whatsapp/dialog360.py (2)
  • 1-1: The import of logger from loguru is appropriate for adding logging capabilities to the module.

  • 27-44: The send_action and mark_as_read methods have been correctly updated to be asynchronous. Ensure that all usages of these methods are updated to handle the asynchronous nature, including using await when calling them.

kairon/chat/handlers/channels/clients/whatsapp/on_premise.py (2)
  • 4-6: The import of AioRestClient is correct and necessary for the asynchronous operations being introduced in this module.

  • 27-68: The methods send_action, get_attachment, mark_as_read, and send_template_message have been updated to be asynchronous. Ensure that all usages of these methods are updated to handle the asynchronous nature, including using await when calling them.

kairon/chat/handlers/channels/clients/whatsapp/cloud.py (2)
  • 8-8: The import of AioRestClient replaces the synchronous requests module, aligning with the PR's goal of introducing asynchronous operations.

  • 104-144: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [63-163]

The methods send, send_json, send_action, get_attachment, mark_as_read, and send_template_message have been updated to be asynchronous. Ensure that all usages of these methods are updated to handle the asynchronous nature, including using await when calling them.

kairon/events/definitions/message_broadcast.py (1)
  • 52-58: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [43-58]

The execute method within the MessageBroadcast class has been correctly updated to be asynchronous. Ensure that all calls to this method are updated to use await and that the event loop is properly managed where this method is invoked.

kairon/shared/channels/broadcast/whatsapp.py (3)
  • 1-10: The imports of asyncio, uuid6, and DoesNotExist are appropriate for the new asynchronous functionality and error handling being introduced.

  • 43-51: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [46-61]

The send method has been correctly updated to be asynchronous. Ensure that all usages of this method are updated to handle the asynchronous nature, including using await when calling it.

  • 88-94: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [91-115]

The __send_using_configuration method has been correctly updated to be asynchronous. Ensure that all usages of this method are updated to handle the asynchronous nature, including using await when calling it.

kairon/chat/handlers/channels/clients/messenger/messenger_client.py (2)
  • 64-72: The make_request method implementation.
    Ensure that the make_request method correctly handles different HTTP methods and responses. It's important to check if the response is being processed correctly and if there's proper error handling for failed requests.
Verification successful

The make_request method in the messenger_client.py file has been successfully extracted. The method takes several parameters, including method, url, request_body, headers, and additional keyword arguments. It uses these parameters to make an asynchronous request via the session.request method. The method also supports streaming responses and custom timeouts, and it retries up to three times if necessary.

The implementation includes popping of is_streaming_resp and timeout from the keyword arguments, which are then passed to the session.request call. The response is logged at the debug level and then returned.

Based on the provided output, the following observations can be made:

  • The method appears to be flexible, handling different HTTP methods as indicated by the method parameter.
  • It allows for both standard and streaming responses.
  • Custom headers and request bodies can be provided.
  • A custom timeout can be set, and a default of three retries is in place.
  • The response is logged for debugging purposes.

However, the output does not show explicit error handling within the make_request method itself. Error handling might be implemented within the session.request method or it might be expected that the caller handles any exceptions raised by make_request.

Given the information available, the make_request method seems to be implemented with consideration for different HTTP methods and responses. For a complete verification, one would need to review the session.request method or the error handling practices in the calling code to ensure robust error handling is in place.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the make_request method is used consistently and correctly throughout the codebase.
rg --type python 'await .+\.make_request\('

Length of output: 76



Script:

#!/bin/bash
# Search for usages of the make_request method across the codebase without specifying file type.
rg 'await .+\.make_request\('

Length of output: 2598



Script:

#!/bin/bash
# Extract the implementation details of the make_request method from the messenger_client.py file.
rg --context 10 'async def make_request\(self,' kairon/chat/handlers/channels/clients/messenger/messenger_client.py

Length of output: 1034

* 204-213: The `generate_appsecret_proof` method. This method is synchronous. If there's no I/O operation or heavy computation, it's fine to keep it synchronous. However, if it's called within an async context, ensure it doesn't block the event loop.
kairon/chat/handlers/channels/msteams.py (1)
  • 56-62: The make_request method implementation.
    Ensure that the make_request method correctly handles different HTTP methods and responses. It's important to check if the response is being processed correctly and if there's proper error handling for failed requests.
Verification successful




The shell script output provides the implementation of the make_request method from both msteams.py and messenger_client.py. Here are the key points from the implementation:

  • Both methods use self.session.request to perform the HTTP request, which is likely a part of an asynchronous HTTP client.
  • They accept various parameters such as method, url, request_body, headers, and timeout.
  • The is_streaming_resp parameter is used to determine if the response should be streamed, and max_retries is set to 3, indicating some level of retry logic.
  • The response is logged using logger.debug(resp) before being returned.

From this implementation, we can infer the following:

  • The method is designed to handle different HTTP methods, as indicated by the method parameter.
  • The method does not directly handle different HTTP responses or errors within its body. Instead, it relies on the caller to handle the response and any exceptions that may arise from self.session.request.
  • Proper error handling for failed requests would need to be implemented in the code that calls make_request, as this method simply returns the response object.

Given the above points, the make_request method appears to be a generic asynchronous request handler that delegates response processing and error handling to the caller. Therefore, the verification of proper error handling and response processing should focus on the code that calls this method, rather than the method itself.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the make_request method is used consistently and correctly throughout the codebase.
rg --type python 'await .+\.make_request\('

Length of output: 76



Script:

#!/bin/bash
# Verify that the make_request method is used consistently and correctly throughout the codebase.
rg 'await .+\.make_request\('

Length of output: 2598



Script:

#!/bin/bash
# Examine the implementation of the make_request method in msteams.py and messenger_client.py
# to verify the handling of different HTTP responses and error handling.
cat kairon/chat/handlers/channels/msteams.py kairon/chat/handlers/channels/clients/messenger/messenger_client.py | rg 'async def make_request'

Length of output: 353



Script:

#!/bin/bash
# View the full implementation of the make_request method in msteams.py and messenger_client.py.
ast-grep --lang python --pattern $'async def make_request($$$)'

Length of output: 409



Script:

#!/bin/bash
# Adjust the pattern to match the full implementation of the make_request method.
ast-grep --lang python --pattern $'async def make_request(self, method, url, $$$)'

Length of output: 428



Script:

#!/bin/bash
# Use cat and rg to extract the full implementation of the make_request method from msteams.py and messenger_client.py.
cat kairon/chat/handlers/channels/msteams.py kairon/chat/handlers/channels/clients/messenger/messenger_client.py | rg -A 10 'async def make_request'

Length of output: 1401

kairon/chat/handlers/channels/messenger.py (4)
  • 5-22: The introduction of MessengerClientOutput and other related imports aligns with the PR's objective to implement asynchronous operations. Ensure that the newly added imports are used within the file and that there are no unused imports which could lead to unnecessary memory consumption and potential confusion.

  • 35-40: The Messenger class constructor now initializes MessengerClientOutput with the page_access_token. This change is consistent with the PR's goal of using an asynchronous client. Verify that the MessengerClientOutput class is fully compatible with the Messenger class and that all methods previously called on the synchronous client are available and correctly implemented in the async client.

  • 178-184: The MessengerBot class has been updated to accept MessengerClientOutput as a parameter in its constructor, which is in line with the shift to asynchronous operations. Ensure that all instances of MessengerBot are updated to pass the new MessengerClientOutput client and that the MessengerBot class methods are compatible with the async client.

  • 412-417: The get_output_channel method in the MessengerHandler class has been updated to instantiate MessengerClientOutput. This change is necessary for the transition to asynchronous operations. Verify that the MessengerClientOutput is correctly instantiated with all required parameters and that the MessengerBot class is prepared to handle the async client.

tests/unit_test/channel_client_test.py (16)
  • 23-33: The test test_send_action has been correctly updated to be asynchronous and uses mock.patch.object to patch the request method of the whatsapp_on_premise.client. The assertions and the use of assert_awaited_once_with are appropriate for the async context.

  • 50-60: The test test_get_attachment is correctly updated to async and properly mocks the request method. The assertion assert_awaited_once_with is used correctly.

  • 64-74: The test test_get_attachment_failure is correctly updated to async and properly mocks the request method. The assertion assert_awaited_once_with is used correctly.

  • 78-88: The test test_mark_as_read is correctly updated to async and properly mocks the request method. The assertion assert_awaited_once_with is used correctly.

  • 92-102: The test test_mark_as_read_failure is correctly updated to async and properly mocks the request method. The assertion assert_awaited_once_with is used correctly.

  • 139-148: The test test_send_template_message_without_namespace correctly raises an AppException when the namespace is not provided. The async update is appropriate.

  • 191-197: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [163-209]

The test test_whatsapp_cloud_send_template_message is correctly updated to async and uses the send method with the autospec=True parameter. The assertions are appropriate for the async context.

  • 211-223: The test test_whatsapp_cloud_send_template_message_without_payload is correctly updated to async and uses the send method with the autospec=True parameter. The assertions are appropriate for the async context.

  • 225-239: The test test_whatsapp_cloud_send_template_message_with_namespace is correctly updated to async and uses the send method with the autospec=True parameter. The assertions are appropriate for the async context.

  • 241-249: The test test_whatsapp_cloud_send_template_message_failure is correctly updated to async and uses the send method with the autospec=True parameter. The assertions are appropriate for the async context.

  • 278-317: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [252-294]

The test test_whatsapp_cloud_send_template_message_with_360dialog is correctly updated to async and uses the aioresponses fixture to mock the async HTTP call. The assertions are appropriate for the async context.

  • 297-311: The test test_whatsapp_cloud_mark_read_360dialog is correctly updated to async and uses the aioresponses fixture to mock the async HTTP call. The assertions are appropriate for the async context.

  • 353-368: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [314-384]

The test test_whatsapp_cloud_send_template_message_with_360dialog_failure is correctly updated to async and uses the aioresponses fixture to mock the async HTTP call. The assertions are appropriate for the async context.

  • 401-422: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [387-416]

The test test_whatsapp_cloud_mark_read_360dialog_failure is correctly updated to async and uses the aioresponses fixture to mock the async HTTP call. The assertions are appropriate for the async context.

  • 425-451: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [419-445]

The test test_whatsapp_cloud_send_message_with_360dialog is correctly updated to async and uses the aioresponses fixture to mock the async HTTP call. The assertions are appropriate for the async context.

  • 467-487: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [448-487]

The test test_whatsapp_cloud_send_message_with_360dialog_failure is correctly updated to async and uses the aioresponses fixture to mock the async HTTP call. The assertions are appropriate for the async context.

Comment on lines 1283 to 1291
def test_whatsapp_valid_text_message_request(aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True

responses.add(
"POST", "https://graph.facebook.com/v13.0/12345678/messages", json={}
aioresponses.add(
method=responses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of responses instead of aioresponses is incorrect for asynchronous tests.

- method=responses.POST,
+ method=aioresponses.POST,

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def test_whatsapp_valid_text_message_request(aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True
responses.add(
"POST", "https://graph.facebook.com/v13.0/12345678/messages", json={}
aioresponses.add(
method=responses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200
def test_whatsapp_valid_text_message_request(aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True
aioresponses.add(
method=aioresponses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200

Comment on lines +36 to +46
@pytest.mark.asyncio
async def test_send_action_failure(self, whatsapp_on_premise):
with mock.patch.object(whatsapp_on_premise.client, 'request') as mock_post:
mock_post.return_value = {"error": {"message": "Message Undeliverable", "code": 400}}
response = await whatsapp_on_premise.send_action(payload={"text": " "})
mock_post.assert_called_once_with(
'POST',
'https://graph.facebook.com/v13.0/messages',
headers=whatsapp_on_premise.auth_args,
json={'text': ' '}, timeout=None
request_body={'text': ' '}, timeout=None, return_json=False, is_streaming_resp=False,
max_retries=3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test test_send_action_failure is correctly updated for async execution. However, the assert_called_once_with should be assert_awaited_once_with to match the asynchronous context.

- mock_post.assert_called_once_with(
+ mock_post.assert_awaited_once_with(

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
@pytest.mark.asyncio
async def test_send_action_failure(self, whatsapp_on_premise):
with mock.patch.object(whatsapp_on_premise.client, 'request') as mock_post:
mock_post.return_value = {"error": {"message": "Message Undeliverable", "code": 400}}
response = await whatsapp_on_premise.send_action(payload={"text": " "})
mock_post.assert_called_once_with(
'POST',
'https://graph.facebook.com/v13.0/messages',
headers=whatsapp_on_premise.auth_args,
json={'text': ' '}, timeout=None
request_body={'text': ' '}, timeout=None, return_json=False, is_streaming_resp=False,
max_retries=3
@pytest.mark.asyncio
async def test_send_action_failure(self, whatsapp_on_premise):
with mock.patch.object(whatsapp_on_premise.client, 'request') as mock_post:
mock_post.return_value = {"error": {"message": "Message Undeliverable", "code": 400}}
response = await whatsapp_on_premise.send_action(payload={"text": " "})
mock_post.assert_awaited_once_with(
'POST',
'https://graph.facebook.com/v13.0/messages',
headers=whatsapp_on_premise.auth_args,
request_body={'text': ' '}, timeout=None, return_json=False, is_streaming_resp=False,
max_retries=3

Comment on lines 1341 to 1349
def test_whatsapp_exception_when_try_to_handle_webhook_for_whatsapp_message(mock_process_message, aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True

responses.add(
"POST", "https://graph.facebook.com/v13.0/12345678/messages", json={}
aioresponses.add(
method=responses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of responses instead of aioresponses is incorrect for asynchronous tests.

- method=responses.POST,
+ method=aioresponses.POST,

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def test_whatsapp_exception_when_try_to_handle_webhook_for_whatsapp_message(mock_process_message, aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True
responses.add(
"POST", "https://graph.facebook.com/v13.0/12345678/messages", json={}
aioresponses.add(
method=responses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200
def test_whatsapp_exception_when_try_to_handle_webhook_for_whatsapp_message(mock_process_message, aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True
aioresponses.add(
method=aioresponses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200

Comment on lines 1391 to 1399
def test_whatsapp_valid_button_message_request(aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True

responses.add(
"POST", "https://graph.facebook.com/v13.0/12345678/messages", json={}
aioresponses.add(
method=responses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of responses instead of aioresponses is incorrect for asynchronous tests.

- method=responses.POST,
+ method=aioresponses.POST,

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def test_whatsapp_valid_button_message_request(aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True
responses.add(
"POST", "https://graph.facebook.com/v13.0/12345678/messages", json={}
aioresponses.add(
method=responses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200
def test_whatsapp_valid_button_message_request(aioresponses):
def _mock_validate_hub_signature(*args, **kwargs):
return True
aioresponses.add(
method=aioresponses.POST,
url="https://graph.facebook.com/v13.0/12345678/messages",
body="success",
status=200

Comment on lines +106 to 121
@pytest.mark.asyncio
async def test_send_template_message(self, whatsapp_on_premise):
namespace = "test_namespace"
name = "test_template_name"
to_phone_number = "9876543210"
with patch("kairon.chat.handlers.channels.clients.whatsapp.on_premise.WhatsappOnPremise.send",
autospec=True) as mock_send:
mock_send.return_value = {
"contacts": [{"input": "+55123456789", "status": "valid", "wa_id": "55123456789"}]}
response = whatsapp_on_premise.send_template_message(namespace=namespace, name=name,
response = await whatsapp_on_premise.send_template_message(namespace=namespace, name=name,
to_phone_number=to_phone_number)
assert response == {"contacts": [{"input": "+55123456789", "status": "valid", "wa_id": "55123456789"}]}
assert mock_send.call_args[0][1] == {'language': {'code': 'en', 'policy': 'deterministic'},
'name': 'test_template_name', 'namespace': 'test_namespace'}
assert mock_send.call_args[0][2] == '9876543210'
assert mock_send.call_args[1] == {'messaging_type': 'template'}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test test_send_template_message is correctly updated to async. However, the send method is patched without specifying the awaited attribute. This should be updated to reflect the asynchronous nature of the method being tested.

- with patch("kairon.chat.handlers.channels.clients.whatsapp.on_premise.WhatsappOnPremise.send",
+ with patch("kairon.chat.handlers.channels.clients.whatsapp.on_premise.WhatsappOnPremise.send.awaited",

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
@pytest.mark.asyncio
async def test_send_template_message(self, whatsapp_on_premise):
namespace = "test_namespace"
name = "test_template_name"
to_phone_number = "9876543210"
with patch("kairon.chat.handlers.channels.clients.whatsapp.on_premise.WhatsappOnPremise.send",
autospec=True) as mock_send:
mock_send.return_value = {
"contacts": [{"input": "+55123456789", "status": "valid", "wa_id": "55123456789"}]}
response = whatsapp_on_premise.send_template_message(namespace=namespace, name=name,
response = await whatsapp_on_premise.send_template_message(namespace=namespace, name=name,
to_phone_number=to_phone_number)
assert response == {"contacts": [{"input": "+55123456789", "status": "valid", "wa_id": "55123456789"}]}
assert mock_send.call_args[0][1] == {'language': {'code': 'en', 'policy': 'deterministic'},
'name': 'test_template_name', 'namespace': 'test_namespace'}
assert mock_send.call_args[0][2] == '9876543210'
assert mock_send.call_args[1] == {'messaging_type': 'template'}
@pytest.mark.asyncio
async def test_send_template_message(self, whatsapp_on_premise):
namespace = "test_namespace"
name = "test_template_name"
to_phone_number = "9876543210"
with patch("kairon.chat.handlers.channels.clients.whatsapp.on_premise.WhatsappOnPremise.send.awaited",
autospec=True) as mock_send:
mock_send.return_value = {
"contacts": [{"input": "+55123456789", "status": "valid", "wa_id": "55123456789"}]}
response = await whatsapp_on_premise.send_template_message(namespace=namespace, name=name,
to_phone_number=to_phone_number)
assert response == {"contacts": [{"input": "+55123456789", "status": "valid", "wa_id": "55123456789"}]}
assert mock_send.call_args[0][1] == {'language': {'code': 'en', 'policy': 'deterministic'},
'name': 'test_template_name', 'namespace': 'test_namespace'}
assert mock_send.call_args[0][2] == '9876543210'
assert mock_send.call_args[1] == {'messaging_type': 'template'}

Comment on lines 2103 to +2106
event = MessageBroadcastEvent(bot, user)
event.validate()
event_id = event.enqueue(EventRequestType.trigger_async.value, config=config)
event.execute(event_id)
result = await event.execute(event_id)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test test_execute_message_broadcast_with_pyscript_timeout should verify the result of the event execution to ensure it matches the expected outcome.

Comment on lines +36 to +50
def __init__(self, page_access_token, **kwargs):
"""
@required:
page_access_token
@optional:
session
api_version
app_secret
"""

self.page_access_token = page_access_token
self.session = AioRestClient(False)
self.api_version = kwargs.get('api_version', DEFAULT_API_VERSION)
self.graph_url = 'https://graph.facebook.com/v{api_version}'.format(api_version=self.api_version)
self.app_secret = kwargs.get('app_secret')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialization of AioRestClient and usage of api_version in the formatted URL.

- self.session = AioRestClient(False)
+ self.session = AioRestClient()

Consider removing the False argument if it's not required for AioRestClient initialization. Also, ensure that api_version is always a valid version string to avoid runtime errors in URL formatting.


Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def __init__(self, page_access_token, **kwargs):
"""
@required:
page_access_token
@optional:
session
api_version
app_secret
"""
self.page_access_token = page_access_token
self.session = AioRestClient(False)
self.api_version = kwargs.get('api_version', DEFAULT_API_VERSION)
self.graph_url = 'https://graph.facebook.com/v{api_version}'.format(api_version=self.api_version)
self.app_secret = kwargs.get('app_secret')
def __init__(self, page_access_token, **kwargs):
"""
@required:
page_access_token
@optional:
session
api_version
app_secret
"""
self.page_access_token = page_access_token
self.session = AioRestClient()
self.api_version = kwargs.get('api_version', DEFAULT_API_VERSION)
self.graph_url = 'https://graph.facebook.com/v{api_version}'.format(api_version=self.api_version)
self.app_secret = kwargs.get('app_secret')

@@ -42,6 +42,7 @@ def __init__(
conversation: Dict[Text, Any],
bot: Text,
service_url: Text,
session: AioRestClient(False),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 NOTE
Codebase verification is a beta feature.

The output from the script shows the instantiation of MSTeamBot but does not include the session parameter in the constructor call. This suggests that the instantiation of MSTeamBot has not been updated to include the new session parameter as required by the changes in the __init__ method.

Based on this information, the following issue has been identified:

  • The instantiation of MSTeamBot in the msteams.py file does not include the new session parameter, which is now required after the changes made to the __init__ method of the MSTeamBot class.
Analysis chain

Addition of session parameter in the __init__ method of MSTeamBot class.
Ensure that the session parameter is documented and that all instantiations of MSTeamBot are updated to pass this new parameter.


It seems there was an issue with the previous script due to an unrecognized file type specifier. Let's correct the script by removing the file type specifier and rerun the search for instances of MSTeamBot instantiation.


The output indicates that there are instances of MSTeamBot being instantiated in the msteams.py file. However, the output does not show the parameters being passed to these instantiations, which is necessary to verify if the new session parameter is included.

To ensure that the session parameter is being correctly passed to all instances of MSTeamBot, we need to examine the full instantiation code. Let's generate a new script to view the lines of code where MSTeamBot is instantiated.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that all instantiations of MSTeamBot include the new session parameter.
rg --type python 'MSTeamBot\('

Length of output: 63



Script:

#!/bin/bash
# Verify that all instantiations of MSTeamBot include the new session parameter.
rg 'MSTeamBot\('

Length of output: 243



Script:

#!/bin/bash
# Extract the lines of code around the instantiation of MSTeamBot to check for the session parameter.
rg -C 3 'MSTeamBot\(' kairon/chat/handlers/channels/msteams.py

Length of output: 662

Comment on lines +78 to 85
token_response = await self.make_request("POST", uri, request_body=payload)

if token_response.ok:
token_data = token_response.json()
access_token = token_data["access_token"]
token_expiration = token_data["expires_in"]
if token_response.status_code == 200:
access_token = token_response["access_token"]
token_expiration = token_response["expires_in"]

delta = datetime.timedelta(seconds=int(token_expiration))
MSTeamBot.token_expiration_date = datetime.datetime.now() + delta
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handling of token response status and access token extraction in _get_headers.
Ensure that the response status and token extraction are handled correctly. The status code check should be against HTTPStatus.OK for clarity.

- if token_response.status_code == 200:
+ if token_response.status_code == HTTPStatus.OK:

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
token_response = await self.make_request("POST", uri, request_body=payload)
if token_response.ok:
token_data = token_response.json()
access_token = token_data["access_token"]
token_expiration = token_data["expires_in"]
if token_response.status_code == 200:
access_token = token_response["access_token"]
token_expiration = token_response["expires_in"]
delta = datetime.timedelta(seconds=int(token_expiration))
MSTeamBot.token_expiration_date = datetime.datetime.now() + delta
token_response = await self.make_request("POST", uri, request_body=payload)
if token_response.status_code == HTTPStatus.OK:
access_token = token_response["access_token"]
token_expiration = token_response["expires_in"]
delta = datetime.timedelta(seconds=int(token_expiration))
MSTeamBot.token_expiration_date = datetime.datetime.now() + delta

Comment on lines +123 to 128
if not send_response != 200:
logger.error(
"Error trying to send botframework messge. Response: %s",
send_response.text,
)
raise Exception(f"Exception while responding to MSTeams:: {send_response.text} and status::{send_response.status_code}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error handling and logging improvements in the send method.
The condition if not send_response != 200: seems to be incorrect. It should be checking for a status code that indicates failure.

- if not send_response != 200:
+ if send_response.status_code != HTTPStatus.OK:

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
if not send_response != 200:
logger.error(
"Error trying to send botframework messge. Response: %s",
send_response.text,
)
raise Exception(f"Exception while responding to MSTeams:: {send_response.text} and status::{send_response.status_code}")
if send_response.status_code != HTTPStatus.OK:
logger.error(
"Error trying to send botframework messge. Response: %s",
send_response.text,
)
raise Exception(f"Exception while responding to MSTeams:: {send_response.text} and status::{send_response.status_code}")

… instead of requests.

2. Fixed unit and integration test cases for the same.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between df9ba31 and afa3717.
Files selected for processing (1)
  • tests/integration_test/chat_service_test.py (10 hunks)
Files skipped from review as they are similar to previous changes (1)
  • tests/integration_test/chat_service_test.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant