[TAN-8120] Sms campaigns#14226
Conversation
luucvanderzee
left a comment
There was a problem hiding this comment.
Amazing work!! A few changes requested but honestly not that many for such a huge huge PR. I didn't do any functional testing and only quickly skipped through the BE tests. Can take another look at those later.
I think it would be great if we could rename the folder and module, otherwise it's going to be really confusing. But of course that will add even more changes to this PR
|
|
||
| # This endpoint is used when a logged in user confirms a pending phone-number | ||
| # change. On success, new_phone is promoted to phone. The phone | ||
| # number isn't part of the auth token, so there's no JWT cookie to refresh. |
There was a problem hiding this comment.
The point of reset_jwt_cookie is not to add update the phone number/email in the token, but to invalidate other sessions. But then that seems to be happening with the token_expiry_key which reset_jwt_cookie does not seem to touch... @jamesspeake how does this work again? And do we need to invalidate the token in this case? I guess not, but wanted to check with you
| ActiveRecord::Base.transaction do | ||
| user.update!(new_phone: new_phone) | ||
| confirmation = user.new_phone_confirmation || user.create_new_phone_confirmation! | ||
| confirmation.reset_code! | ||
| campaign = EmailCampaigns::Campaigns::NewPhoneConfirmation.first_or_create! | ||
| EmailCampaigns::DeliveryService.new.send_now_to_user(campaign, user, { code: confirmation.code }) | ||
|
|
||
| confirmation.update!(code_sent_at: Time.zone.now) | ||
| ExpireConfirmationCodeOrDeleteJob.set( | ||
| wait_until: confirmation.expiration_at | ||
| ).perform_later( | ||
| user.id, | ||
| NewPhoneConfirmation.name, | ||
| confirmation.code | ||
| ) | ||
| LogActivityJob.perform_later(user, 'received_confirmation_code', user, Time.now.to_i, payload: { new_phone: new_phone }) | ||
| end |
There was a problem hiding this comment.
I propose we keep it the same and look into this transaction stuff when we look at the other issue related to being able to request too many codes if you do it really fast
| # subject_multiloc is the email subject for content-configurable emails and | ||
| # the admin-facing label for SMS campaigns (SMS has no subject line). | ||
| attribute :subject_multiloc, if: proc { |object| | ||
| content_configurable?(object) || object.sms? |
There was a problem hiding this comment.
If sms has no subject line, should the check not be || !object.sms?
There was a problem hiding this comment.
Maybe the comment is confusing, but what I meant is that the SMS itself has no subject line but the campaign does (it's just an internal label for the admin).
There was a problem hiding this comment.
Maybe we can clarify the comment? I still don't really get it.
| parsed = Phonelib.parse(to) | ||
| raise Error, "Invalid phone number: #{to}" unless parsed.valid? | ||
|
|
||
| parsed.e164 |
There was a problem hiding this comment.
What is this parser doing exactly? Maybe we can add a comment?
There was a problem hiding this comment.
It just checks the string we give it to see if it's a valid phone number and gives us some useful methods like .valid, .country ..etc. I've also added a comment.
| const [screenReaderMessage, setScreenReaderMessage] = useState<string>(''); | ||
| const { formatMessage } = useIntl(); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
Instead of this useEffect and state variable, can't you just render the screenReaderMessage conditionally without adding extra state?
There was a problem hiding this comment.
This useEffect is just copied from the email approach to be honest. It seems to be part of a relatively new change for a11y.
There was a problem hiding this comment.
Ok, let's make a note to replace it in both places because I can't really see any reason for this to work like this
jamesspeake
left a comment
There was a problem hiding this comment.
Looks good. A few comments to look at and I've not looked in quite as much detail at the FE as it seemed a lot of reusing the same patterns.
Not sure what the e2e coverage is like for email campaigns - Assume you've tested the email campaigns interface well given how much refactoring there has been there with name changes / endpoint changes.
Have the changes to the consent screen been run past product? From reading the code it seems that even if it's turned off, there is a small change to the layout?
|
|
||
| new_phone = request_code_phone_change_params[:new_phone] | ||
| if new_phone.blank? | ||
| render json: { errors: { new_phone: [{ error: 'cannot be blank' }] } }, status: :unprocessable_entity |
There was a problem hiding this comment.
Not sure if we use these on the FE to display messages, but usually I'd make these errors identifiers that can be used to map to translations eg cannot_be_blank
There was a problem hiding this comment.
We indeed do, it's just that it was pre existing and I just reused it. There are others like it like this and would probably be better to solve in a different PR.
| ActiveRecord::Base.transaction do | ||
| user.update!(new_phone: new_phone) | ||
| confirmation = user.new_phone_confirmation || user.create_new_phone_confirmation! | ||
| confirmation.reset_code! | ||
| campaign = EmailCampaigns::Campaigns::NewPhoneConfirmation.first_or_create! | ||
| EmailCampaigns::DeliveryService.new.send_now_to_user(campaign, user, { code: confirmation.code }) | ||
|
|
||
| confirmation.update!(code_sent_at: Time.zone.now) | ||
| ExpireConfirmationCodeOrDeleteJob.set( | ||
| wait_until: confirmation.expiration_at | ||
| ).perform_later( | ||
| user.id, | ||
| NewPhoneConfirmation.name, | ||
| confirmation.code | ||
| ) | ||
| LogActivityJob.perform_later(user, 'received_confirmation_code', user, Time.now.to_i, payload: { new_phone: new_phone }) | ||
| end |
There was a problem hiding this comment.
If it fails, the whole job should get retried though? If it mirrors email then probably fine as it is and low risk that it will fail as any failure in send_now_to_user I think will just be written to the delivery record
There was a problem hiding this comment.
I think this works in a slightly different way to Hooks::MailgunEventsController as that one has to switch tenants, but worth a quick look to see if it makes sense for code to maybe share the same language eg Sms::Hooks::EventsController - are these referred to as webhooks? Not a blocker, but feels we're describing both in different ways
| key={deliveries.data.map((delivery) => delivery.id).join()} | ||
| > | ||
| {deliveries.data.map((delivery) => ( | ||
| <TableRow delivery={delivery} key={delivery.id} /> |
There was a problem hiding this comment.
Probably not a conversation for this PR, but wonder why (from an accessibility perspective) we don't use HTML tables where we obviously have tabular data
Changelog
Added
Changed
Technical
smsfeature flag holding Twilio credentials in tenant settings, a Twilio provider with delivery-status callbacks, and an SMS delivery model.email/andsms/modules.For translators