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

Add ChannelType.check_credentials and use for Twilio claims and updates #4275

Merged
merged 10 commits into from
Mar 13, 2023

Conversation

norkans7
Copy link

@norkans7 norkans7 commented Jan 30, 2023

Addresses #4227

Old UI
Monosnap RapidPro - Visually build nationally scalable mobile applications 2023-02-02 12-43-31

New UI
Monosnap Maintenance 2023-02-02 12-46-31

@codecov
Copy link

codecov bot commented Jan 30, 2023

Codecov Report

Merging #4275 (1ae190e) into main (97b7ded) will not change coverage.
The diff coverage is 100.00%.

@@            Coverage Diff            @@
##              main     #4275   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          472       472           
  Lines        26130     26178   +48     
=========================================
+ Hits         26130     26178   +48     
Impacted Files Coverage Δ
temba/channels/models.py 100.00% <100.00%> (ø)
temba/channels/types/twilio/type.py 100.00% <100.00%> (ø)
temba/channels/types/twilio/views.py 100.00% <100.00%> (ø)
...ba/channels/types/twilio_messaging_service/type.py 100.00% <100.00%> (ø)
temba/channels/types/twilio_whatsapp/type.py 100.00% <100.00%> (ø)
temba/channels/views.py 100.00% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@@ -141,6 +141,9 @@ def get_update_form(self):
return UpdateChannelForm
return self.update_form

def check_credentials(self, cleaned_data):
Copy link
Member

Choose a reason for hiding this comment

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

I think we want something more like...

def check_credentials(self, config: dict) -> bool:

so then we have something like this on Channel

def check_credentials(self) -> bool:
    return self.type.check_credentials(self.config)

and then at any time you can verify the credentials of a channel

Copy link
Author

Choose a reason for hiding this comment

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

ok

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.add_config_field(
Copy link
Author

Choose a reason for hiding this comment

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

I exatended the channel update to include the credentials field for for the channel type

Comment on lines 350 to 367
def clean(self) -> Dict[str, Any]:
account_sid = self.cleaned_data.get("account_sid", None)
account_token = self.cleaned_data.get("account_token", None)

try:
client = TwilioClient(account_sid, account_token)

# get the actual primary auth tokens from twilio and use them
account = client.api.account.fetch()
self.cleaned_data["account_sid"] = account.sid
self.cleaned_data["account_token"] = account.auth_token
except Exception: # pragma: needs cover
raise ValidationError(
_("The Twilio account SID and Token seem invalid. Please check them again and retry.")
)

return self.cleaned_data
Copy link
Author

@norkans7 norkans7 Jan 30, 2023

Choose a reason for hiding this comment

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

We need to clean the credentials, and in case of Twilio we need to save the primary token of the account in case we were given others

So each channel type update form will do its own validation if needed

@@ -74,6 +74,7 @@ class IVRProtocol(Enum):
show_public_addresses = False

update_form = None
verify_credentials = False
Copy link
Author

Choose a reason for hiding this comment

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

For now we'll be switching this flag once we have added the channel type check_credentials

Copy link
Member

Choose a reason for hiding this comment

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

could we just have ChannelType.check_credentials default to True ?

Copy link
Author

Choose a reason for hiding this comment

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

I thought we'll remove the flag once the method is implemented for all channel type we have but I guess that is fine to have that default to True

"account_sid",
forms.CharField(
max_length=128,
label=_("Your Twilio Account SID"),
Copy link
Member

Choose a reason for hiding this comment

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

Remove Your

"auth_token",
forms.CharField(
max_length=128,
label=_("Your Twilio Account Auth Token"),
Copy link
Member

Choose a reason for hiding this comment

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

Remove Your

account_sid = self.cleaned_data.get("account_sid", None)
account_token = self.cleaned_data.get("account_token", None)

try:
Copy link
Member

Choose a reason for hiding this comment

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

We want to use check_credentials here as well - calling it with the config + these new values, e.g.

self.instance.check_credentials({**self.instance.config, "account_sid": account_sid, "auth_token": auth_token})

Copy link
Author

Choose a reason for hiding this comment

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

the main thing here is we want to grab the primary credentials so check_credentials is not enough alone

otherwise we would have relied on the base class clean from that use the method that way

https://github.com/nyaruka/rapidpro/pull/4275/files#diff-6aff4c43504e1e48908ece639cb7e6a4d98608c0ce761fc2bfd298b10a57ddedR674-R680

@@ -670,6 +671,13 @@ def add_config_field(self, config_key: str, field, *, default):
self.fields[config_key] = field
self.config_fields.append(config_key)

def clean(self) -> Dict[str, Any]:
Copy link
Member

Choose a reason for hiding this comment

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

python 3.10 prefers dict[str, Any].. we need to come up with a style guide for typing in Python. I've generally avoided complex types and probably would have just used dict here, but we need some rules to keep ourselves consistent.

@@ -74,6 +74,7 @@ class IVRProtocol(Enum):
show_public_addresses = False

update_form = None
verify_credentials = False
Copy link
Member

Choose a reason for hiding this comment

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

could we just have ChannelType.check_credentials default to True ?

@rowanseymour rowanseymour changed the title Twilio update Add ChannelType.check_credentials and use for Twilio claims and updates Jan 31, 2023
cleaned_data = super().clean()
channel_type = Channel.get_type_from_code(self.object.channel_type)
if channel_type.verify_credentials and not channel_type.check_credentials(cleaned_data):
raise ValidationError(_("Error checking credentials for channel type %s" % self.object.channel_type))
Copy link
Member

Choose a reason for hiding this comment

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

This should be a user facing error message like Channel credentials don't appear to be valid.

@norkans7 norkans7 force-pushed the twilio-update branch 3 times, most recently from 6ad473c to d38c66f Compare February 2, 2023 10:40
@norkans7 norkans7 marked this pull request as ready for review February 2, 2023 10:53
Copy link

@susanm74 susanm74 left a comment

Choose a reason for hiding this comment

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

@norkans7 i don't know this area of the product super well, so just dropping a comment (vs approving) after reviewing the changes to say LGTM

@norkans7
Copy link
Author

norkans7 commented Mar 7, 2023

@rowanseymour @ericnewcomer when you get a chance please review

)

# We override the clean method for Twilio we need to make sure we grab the primary auth tokens
def clean(self) -> Dict[str, Any]:
Copy link
Member

Choose a reason for hiding this comment

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

def clean(self) -> dict[str, Any]:
    """
    We override the clean method for Twilio we need to make sure we grab the primary auth tokens
    """

@@ -424,6 +425,18 @@ def add_config_field(self, config_key: str, field, *, default):
self.fields[config_key] = field
self.config_fields.append(config_key)

def transform_form_data_credentials_config(self, cleaned_data: dict, extra: dict) -> dict:
Copy link
Member

Choose a reason for hiding this comment

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

this doesn't need to be a separate method.. or have the longest name in the repo lol


def clean(self) -> dict[str, Any]:
cleaned_data = super().clean()
credentials_config = self.creds_config(cleaned_data, {})
Copy link
Member

Choose a reason for hiding this comment

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

Am still confused by this line which is just cloning cleaned_data

Shouldn't this be self.object.config | cleaned_data ? i.e. merge existing config and new values from form?

Copy link
Member

Choose a reason for hiding this comment

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

The idea is that channel types subclass this and override creds_config?

In which case make it something like...

def to_config(self, data) -> dict:
    return {}

That just returns form data transformed into config values, and do the merging in clean and form_valid with self.instance.config | self.to_config(cleaned_data)

Copy link
Author

Choose a reason for hiding this comment

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

Right the idea is to allow the subclass to override that in case the form fields names do not match the config keys.
For Twilio so far added I see I had changed to have them match so I think the to_config is probably not needed for now.

@norkans7
Copy link
Author

norkans7 commented Mar 8, 2023 via email

@rowanseymour rowanseymour merged commit 90bb50b into main Mar 13, 2023
@rowanseymour rowanseymour deleted the twilio-update branch March 13, 2023 16:47
@github-actions github-actions bot locked and limited conversation to collaborators Mar 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants